diff --git a/reader/views.py b/reader/views.py index db410d7f72..112279308f 100644 --- a/reader/views.py +++ b/reader/views.py @@ -1935,12 +1935,15 @@ def _internal_do_delete(request, link_id_or_ref, uid): j = json.loads(j) skip_check = request.GET.get("skip_lang_check", 0) + override_preciselink = request.GET.get("override_preciselink", 0) if isinstance(j, list): res = [] for i in j: try: if skip_check: i["_skip_lang_check"] = True + if override_preciselink: + i["_override_preciselink"] = True retval = _internal_do_post(request, i, uid, **kwargs) res.append({"status": "ok. Link: {} | {} Saved".format(retval["ref"], retval["anchorRef"])}) except Exception as e: diff --git a/sefaria/model/link.py b/sefaria/model/link.py index 351afd6613..7efe4a0582 100644 --- a/sefaria/model/link.py +++ b/sefaria/model/link.py @@ -90,20 +90,39 @@ def _validate(self): def _pre_save(self): if getattr(self, "_id", None) is None: # Don't bother saving a connection that already exists, or that has a more precise link already - if self.refs != sorted(self.refs) and hasattr(self, 'charLevelData'): - self.charLevelData.reverse() - orig_refs = self.refs - self.refs = sorted(self.refs) #make sure ref order is deterministic - if orig_refs != self.refs and getattr(self, "versions", False) and getattr(self, "displayedText", False): - #if reversed self.refs, make sure to reverse self.versions and self.displayedText - self.versions = self.versions[::-1] - self.displayedText = self.displayedText[::-1] + if self.refs != sorted(self.refs): + if hasattr(self, 'charLevelData'): + self.charLevelData.reverse() + if getattr(self, "versions", False) and getattr(self, "displayedText", False): + # if reversed self.refs, make sure to reverse self.versions and self.displayedText + self.versions = self.versions[::-1] + self.displayedText = self.displayedText[::-1] + self.refs = sorted(self.refs) # make sure ref order is deterministic samelink = Link().load({"refs": self.refs}) + if not samelink: + #check for samelink section level vs ranged ref + oref0 = text.Ref(self.refs[0]) + oref1 = text.Ref(self.refs[1]) + section0 = oref0.section_ref() + section1 = oref1.section_ref() + if oref0.is_range() and oref0.all_segment_refs() == section0.all_segment_refs(): + samelink = Link().load({"$and": [{"refs": section0}, {"refs": self.refs[1]}]}) + elif oref0.is_section_level(): + ranged0 = text.Ref(f"{oref0.all_segment_refs()[0]}-{oref0.all_segment_refs()[-1]}") + samelink = Link().load({"$and": [{"refs": ranged0.normal()}, {"refs": self.refs[1]}]}) + elif oref1.is_range() and oref1.all_segment_refs() == section1.all_segment_refs(): # this is an elif since it anyway overrides the samelink see note in 4 lines + samelink = Link().load({"$and": [{"refs": section1}, {"refs": self.refs[0]}]}) + elif oref1.is_section_level(): + ranged1 = text.Ref(f"{oref1.all_segment_refs()[0]}-{oref1.all_segment_refs()[-1]}") + samelink = Link().load({"$and": [{"refs": ranged1.normal()}, {"refs": self.refs[0]}]}) + #note: The above code neglects the case where both refs in the link are section or ranged and there is a ranged/section link in the db with the opposite situation on both refs. + if samelink: if hasattr(self, 'score') and hasattr(self, 'charLevelData'): samelink.score = self.score samelink.charLevelData = self.charLevelData + samelink.save() raise DuplicateRecordError("Updated existing link with the new score and charLevelData data") elif not self.auto and self.type and not samelink.type: @@ -130,7 +149,12 @@ def _pre_save(self): if preciselink: # logger.debug("save_link: More specific link exists: " + link["refs"][1] + " and " + preciselink["refs"][1]) - raise DuplicateRecordError("A more precise link already exists: {} - {}".format(preciselink.refs[0], preciselink.refs[1])) + if getattr(self, "_override_preciselink", False): + preciselink.delete() + self.generated_by = self.generated_by+'_preciselink_override' + #and the new link will be posted (supposedly) + else: + raise DuplicateRecordError("A more precise link already exists: {} - {}".format(preciselink.refs[0], preciselink.refs[1])) # else: # this is a good new link if not getattr(self, "_skip_lang_check", False): diff --git a/static/css/s2.css b/static/css/s2.css index eb242197e3..8c33eb0f55 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5172,7 +5172,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus color: var(--inline-link-blue); } .segment .he sup { - font-size: 0.5em; + font-size: 0.6em; } .segment sup:hover { text-decoration: underline; @@ -5189,7 +5189,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .segment sup.itag { text-decoration: none; font-family: var(--hebrew-sans-serif-font-family); - font-size: 0.6em; + font-size: 0.5em; line-height: 1; color: var(--inline-link-blue); } @@ -6132,6 +6132,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .textListTextRangeBox { position: relative; } + .textListTextRangeBox .textRange{ margin-bottom: 0; padding-bottom: 0; diff --git a/static/js/TextList.jsx b/static/js/TextList.jsx index f54908937e..01c274cf69 100644 --- a/static/js/TextList.jsx +++ b/static/js/TextList.jsx @@ -12,7 +12,7 @@ import Sefaria from './sefaria/sefaria'; import PropTypes from 'prop-types'; import TextRange from './TextRange'; import Component from 'react-class'; - +import classNames from 'classnames'; class TextList extends Component { constructor(props) { @@ -216,7 +216,8 @@ class TextList extends Component { key={i + link.anchorRef} />); } else { var hideTitle = link.category === "Commentary" && this.props.filter[0] !== "Commentary"; - return (
+ const classes = classNames({ textListTextRangeBox: 1, typeQF: link.type.startsWith('quotation_auto')}); + return (