RMuseum User login/logout/register/... login loginViewModel LoggedOnUserModel renew an expired session user session id LoggedOnUserModel options service constructor HTTP dates only have one-second precision - truncate before using a value for Last-Modified/If-Modified-Since comparisons, or a DB value with sub-second precision would always fail the >= comparison against a client's necessarily-truncated header gets specified publish artifact item info (including images + attributes) get all published artifacts (including CoverImage info but not items info) get list of artifact statuses user can see get all artifacts visible by user (including CoverImage info but not items info) gets specified publish artifact info (including CoverImage + images) get specific artifact info with limited number of images get artifact images according to start and count params remove unpublished artifact having no notes and not bookmarked gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag gets specified publish artifact info (including CoverImage + images) gets specified publish artifact info (including CoverImage + images) gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag edit artifactt master record (user should have additional permissions artifact:awaiting and artifact:publish to change status of artifact) editing related collections such as images and attributed or complex properties such as CoverImage is ignored Set Artifact Cover Item Index get tag bundle by frindly url add new tag only name is processed get all tags edit tag editable fields are limited changes order of tags based on their position in artifacts up / down the other tag which its Order got replaced with the input id changes order of tags based on their position in artifact items up / down the other tag which its Order got replaced with the input id get tag value bundle by frindly url get tag value by frindly url add new tag value to artifact only name is processed edit artifact attribute value editable fields are limited apply on all same value tags remove tag from artifact add new tag value to item only tag.id is processed edit item attribute value editable fields are limited apply on all same value tags remove tag from item changes order of tag values based on their position in an artifact up / down the other tag which its Order got replaced with the input id changes order of tag values based on their position in an item up / down the other tag which its Order got replaced with the input id update an artifact item info gets specified publish artifact item info (including images + attributes) Add new artifact (multipart/form-data) const data = new FormData(); data.append('name', 'تست'); data.append('description', ''); data.append('srcUrl', ''); data.append('picTitle', 'تست'); data.append('picDescription', ''); data.append('file', this.file); data.append('picSrcUrl', ''); import from external resources pdf/loc/princeton/harvard/qajarwomen/hathitrust/penn/cam/bl/folder/walters/cbl/append/iiif 119/foldername golestan-baysonghori/artifact id keep alive (for import background service) true retry import due to a bug in loc json outputs some artifacts with more than 1000 pages were downloaded incompletely 123456 bookmark artifact bookmark item item bookmarks info fav artifact fav item artifact bookmarks info item bookmarks info user bookmarks user favorites add note for artifact add note for artifact item Edit User Notes a note can not be edited by a user other than its owner or another using having note:moderate permission only htmlContent is processed delete user notes 1. a note can not be deleted by a user other than its owner or another using having note:moderate permission 2. all notes which have refernce to deleting note (sent in reply to it) would be deleted irrelevant of their ownership list of notes deleted get private notes for artifact get private notes for artifact get private notes for artifact item get public notes for artifact item user public notes user public notes all users public notes report a public note id of saved report Get a list of reported notes delete a report for abuse in public user notes delete a reported user note (accept the complaint) suggest ganjoor link get suggested ganjoor links finds next unsynchronized suggested link with an aleady synched one from the artificat if exists, return value might be null or an array with length 1 or 2 (has paging-headers) has paging-headers return value might be null or an array with length 1 or 2 review suggested ganjoor link mark suggested ganjoor link as synchronized display ogn page remove ganjoor link toc / temporary one time api / to be removed get suggested pinterest links suggest pinterest link for ganjoor review suggested ganjoor pinterest link mark suggested pinterest ganjoor link as synchronized remove pinterest link start filling GanjoorLink table OriginalSource values start removing original images search artifacts search artifact items start setting an artifact items as a category poems text original source upload artifact to external server create images for ganjoor create images for ganjoor - offline add a new item to an artifact readonly mode constructor Artifact Service IUserPermissionChecker instance IMemoryCache Captcha service IAppUserService instance Configuration Audit Log Controller Base constructor user contribution daily stats poemcorrections sectioncorrections catcorrections suggestedsongs quoteds discussions recitations museumlinks pinterestlinks poetspeclines poetpix usernotes, users datatype grouped by user poemcorrections sectioncorrections catcorrections suggestedsongs quoteds discussions recitations museumlinks pinterestlinks poetspeclines poetpix usernotes summed up stats of data poemcorrections sectioncorrections catcorrections suggestedsongs quoteds discussions recitations museumlinks pinterestlinks poetspeclines poetpix usernotes users (Days and UserIds are invalid) service IMemoryCache constructor returns all donations donation by id add donation + regenerate donations page update donation date and donorname + regenerate donations page delete donation + regenerate donations page returns all expenses expense by id add expense + regenerate donations page update expense date and description + regenerate donations page delete expense one time import regenerate donations page is account info settings is on or off (for deciding to regenerate donations page based on it) donation service queued ftp uploads process ftp queue reset ftp queue FTP Service constructor get published categories get all categories get category by id add a new faq category update an existing faq category delete a faq category get published pinned items get published category items get all category items get item by id get published item by id add a new faq item update an existing faq item delete a faq item FAQ Service constructor get list of published poets without their biography get list of books (GanjoorCat entries whose CatType is Book, sorted alphabetically by name), optionally filtered by (part of) name and/or poet id. Not to be confused with GetBooksAsync()/"books" above, which lists GanjoorCat entries by their (separate, legacy) BookName field for cover-image generation. optional, only books whose name contains this optional, only books belonging to this poet gets list of poets grouped by centuries (first one is the pinned ones) get list of all poets (including unpublished ones) with their bio poet by id poet by url update poet info (except for image) regenerate half centuries create new poet starts deleting poet job ` get poet image with png ext sample: hafez get poet image sample: hafez set poet image poet image cat by id cat by full url set category extra info only RImageId field is valid generate missing book covers list of books batch rename cat poems batch resulg category poems set category poems language tag set category poems language tag fill missing poem formats start assigning poem rhythms delete a category Start Finding Missing Rhythms generate category toc directly insert generated TOC start generating sub cats TOC regenerate TOCs page by url TopRated, Oldest, or Newest. Only applies to poem pages. modify page modify poem => only these fields: NoIndex, RedirectFromFullUrl, MixedModeOrder clean cache by id delete page older versions of a page (modifications history except for current version) get old version of page page url by id get redirect url for a url get poem by id not implemented yet next/previous get poem verses by id get poem by url not implemented yet not implemented yet not implemented yet next/previous get poem recitations (PlainText/HtmlText are intentionally empty) get user upvoted recitations of a poem get poem images (PlainText/HtmlText are intentionally empty) get poem songs Get Poem Comments TopRated, Oldest, or Newest Rate a comment (like / dislike / clear rating) comment id +1: like, -1: dislike, 0: remove previous rating get the logged on user's own rating values for a poem's comments (meant to be merged client-side into an already fetched, anonymously cacheable comment list) poem id Get Section Related ones zero or less than it means all find poem section rhyming letters delete a poem section (section should not be linked to a poem verse of types other than paragraphs or comments) delete a poem send poem corrections delete unreviewed user corrections for a poem returns last unreviewed correction from the user for a poem get list of user suggested corrections get list of all suggested corrections userId effective corrections for poem get correction by id get next unreviewed correction moderate poem correction break a poem from a verse forward id of new poem suggest song for poem get next unreviewed song get song by id history of suggested songs by a user user suggested songs review song modify a published song delete a poem song by id directly insert a poem related song get a random poem from hafez get a random poem from a poet (c.ganjoor.net replacement), 0 means random poet, حافظ (2)، خیام (3)، ابوسعید ابوالخیر (26)، صائب (22)، سعدی (7)، باباطاهر (28)، مولوی (5)، اوحدی (19)، خواجو (20)، شهریار (35)، عراقی (21)، فروغی بسطامی (32)، سلمان ساوجی (40)، محتشم کاشانی (29)، امیرخسرو دهلوی (34)، سیف فرغانی (31)، عبید زاکانی (33)، هاتف اصفهانی (25) یا رهی معیری (41) get a single comment information (only published comments, replies are not included) get recent comments get logged on users recent comments get awaiting comments delete awaiting comment publish awaiting comment post new comment edit user's own comment link or unlink user's own comment to a coupletIndex couplet summary for linked comment delete user's own comment report a comment id of reported record delete a report (without deleting corresponding comment) get list of reported comments delete reported other users comment imports data from ganjoor SQLite database (form file) import category data from ganjoor SQLite database (form file) Apply corrections from sqlite export a poet to sqlite database start exporting all poets start exporting all published Ganjoor data to the public git-tracked JSON data set start exporting the Tajik (Cyrillic) overlay data to its own, separate public git-tracked JSON data set — only poets/categories/poems with an actual Tajik translation are included (re)build local Ganjoor content from a public data export tree (local folder or HTTP) — intended for local development use, not for production servers Get user public profile Get Similar Poems accroding to prosody and rhyme informations cannot be empty can be empty send 0 for all send 0 for all return value is not complete or valid for some parts, you should use only the valid parts! language tagged poem sections fa-IR, ar, ... 0 means all poets search returns ganjoor metre list ordered by rhythm find poem rhyme analysis poem to find its prosody information examine site pages for broken links examine comments for long links start filling poems couplet indices refill couplet indices for poem fill section couplet counts regenerate poem full titles to fix an old bug start finding rhymes separate verses in poem.PlainText with Environment.NewLine instead of SPACE if it is 0 it is ignored start building sitemap build tj.ganjoor.net site map regenerate stats page start updating mundex page switch bookmark if you send a negative number it means you are trying to bookmark a comment switch bookmark and return bookmark id ('0' for switching off a bookmark) if you send a negative number it means you are trying to bookmark a comment Bookmark couplet if it is not delete bookmark modify bookmark private note get poem user bookmarks (only Id, CoupletIndex and DateTime are valid in the output view model) is the poem couplet is bookmarked by user user bookmarks a phrase to be searched through user private notes start generating related sections info for wholepoem sections get next ganjoor poem probable metre get a list of ganjoor poems probable metres save ganjoor poem probable metre dismiss ganjoor poem probable metre Finding Category Poems Duplicates manually add a duplicate for a poems delete a duplicate list of category saved duplicated poems start removing category duplicates get couplet sections get all poem sections regenerate poem sections (dangerous: wipes out existing data) update related sections manually regenerate category related sections category id get a specific poem section start band couplets fix returns last unreviewed correction from the user for a section section id send a correction for a section moderate poem section correction delete unreviewed user corrections for a poem section get section correction by id get next unreviewed correction for poem sections get list of user suggested corrections get list of all suggested corrections effective corrections for section transfer poems and sections from a meter to another one and delete the source meter get poem tags ordered by LunarDateTotalNumber then by Id add poem geo tag update poem tag delete poem tag get a categoty poem tags get the network of people relevant to this category/work (every person tagged in a poem under its subtree, plus their relatives/affiliates one hop out) - for the "شخصیت‌ها" tab on a category/poet page synchronize https:://naskban.ir links (logs in and then out to naskban.ir using auth info) number of synched links mark naskban links for poems of a categiory and its children as human reviewed mark naskban links a text original for a category import naskban ganjoor matchings discover poet naskban paper sources delete poem related naskban images by url import paper sources from museum get paper sources for a catgeory get category poem related images extracting quoted poems regenerate related poems pages regenerate two poets similar page discover related poems get quoted poems for a poem two poems quoted records get quoted poems get quoted by id insert quoted update quoted delete quoted suggest new quote (for normal users) get list of user suggested corrections next unmoderated quoted poem moderate quoted poems digital source from tag tag category with source update digital sources stats add new page build word counts one time fixer for word counts new RowNmbrInCat fields one time fixer for category word count summries category word counts can be empty category words summary comparison of word counts for poets can be null or can be extracted from poetId is not necessary unless you mean a specific poet and do not provide catId CategoryWordCount for a specific term in a category re-runs the Tajik poet/category HTML generators for every already-imported poet and category page, overwriting their stored TajikHtmlText. Needed one-time after changing either generator function (e.g. adding poem excerpts to the table of contents), since the normal SQLite import skips pages that already exist and never refreshes them. Runs as a background job - check the Admin area's LongRunningJobs page for progress. recovery job: regenerates TajikHtmlText for Tajik poems whose stored HTML currently contains a link (the signature left by the CatPage id corruption bug) - use after RegenerateTajikCatAndPoetHtmlText (or anything else) may have corrupted poem pages. Check the Admin area's LongRunningJobs page for progress. tajik poets tajik page by url search tajik poems fill couplet summaries using open ai fill poem summaries using open ai geo tag poems using AI send cat corrections delete unreviewed user cat corrections for a poem returns last unreviewed correction from the user for a cat get list of user suggested cat corrections get list of all suggested cat corrections userId effective corrections for a cat get cat correction by id get next unreviewed cat correction moderate cat correction readonly mode Ganjoor Service IAppUserService instance for client IP resolution Image Service IMemoryCache aggressive cache Configuration related people (family tree / person tagging) service - used here only for the category-scoped "cat/{id}/persongraph" endpoint; everything else about people lives in GanjoorRelatedPersonController constructor returns list of suggested photos for a poet poet id returns a single suggested photo return the next unpublished suggested line for poets add a suggestion for poets photos (send form) with these fields: poetId, title, description, srcUrl and an image attachment modify a suggestion for poets photos reject a suggestion for poets photos delete published suggested photo Poets Photos Service constructor returns list of suggested spec lines for a poet poet id returns specific suggested line for poets return the next unpublished suggested line for poets add a suggestion for poets spec lines modify a suggestion for poets spec lines reject a suggestion for poets spec lines delete published suggested spec line Ganjoor Service constructor related people (family tree / person tagging) - reads are anonymous; the only write paths are the suggestion queue below (any logged-in user may suggest an edit to an existing person, same as suggesting a poem correction; a moderator reviews it the same way a poem correction is reviewed). There is no direct-edit endpoint for GanjoorRelatedPerson. get all people get people who caption a family tree - the entry points for browsing family trees. Registered before the "{id}" route below (and constrained to int there) so this literal segment isn't swallowed as an id. get the whole known network of people (every person with at least one kinship edge or non-family tie, plus every one of those edges/ties), for the force-directed "ontology" explorer opened via the PeopleExplorer.open() modal (formerly the standalone page /PeopleGraph). Registered before the "{id}" route below (and constrained to int there) so this literal segment isn't swallowed as an id. get person by id get a person along with all their kinship/affiliation edges, for the read-only person/family-tree browsing page get the poems tagged with this person get the whole connected kinship component reachable from this person (ancestors, descendants, spouses, siblings), for the interactive family-tree chart suggest an edit to an already-approved person's own fields - any logged-in user, same as suggesting a poem correction. Goes into the pending queue; does not change the person. get the next unreviewed person-edit suggestion, for the moderator queue - same permission as reviewing a poem correction apply a moderator's decision to a pending person-edit suggestion - same permission as moderating a poem correction get a single kinship edge by its own id, with both sides' names resolved - used by /SuggestPersonRelationEdit/{relationId} to show what it's about get a single affiliation edge by its own id, with both sides' names resolved - used by /SuggestPersonRelationEdit?affiliationId={affiliationId} to show what it's about suggest an addition, change or removal of a kinship edge - any logged-in user, same as suggesting a person edit. Goes into the pending queue; does not change anything. get the next unreviewed relation-edit suggestion, for the moderator queue - same permission as reviewing a person edit apply a moderator's decision to a pending relation-edit suggestion - same permission as moderating a person edit person service constructor get all locations get location by id add new location update an existing location readonly mode Configuration location service constructor returns image stream with image/jpeg MIME type norm/orig/thumb returns image stream with image/webp MIME type Rotate Image in 90 deg. multiplicants: 90, 180 or 270 replace an image Generate Cropped Image Based On ThumbnailCoordinates For Notes id of cropped image constructor Artifact Service golha collection programs collection id program tracks Catalogue Service constructor Notifications controller constructor get all numberings get numbering by id get all numberings related to a category category id get all numbering patterns for a couplet add new numbering update an existing numbering recount a numbering generate missing default numberings and start counting readonly mode Configuration numbering service constructor returns paginated published recitations (if poetId or catId is non-zero its ordered by poemId ascending if not it is ordered by publish date descending) if PageSize is -1 or is more than 1000 it resets to 1000 empty: no search term, non-empty: searches within AudioArtist, AudioTitle, poem.FullTitle and poem.PlainText simultaneously get published recitation by id creates an RSS file from recent published recitations Gets audio narrations, user must have recitation::moderate permission to be able to see all users narrations default: false, user must have recitation::moderate permission to be able to see all users narrations default: -1, unfiltered additional headers: paging-headers, audio-upload-enabled get the corresponding mp3 file for the narration it could be protected (Authorized), but I guess I would have problems with available client components support, so I preferred it to be anonymous, as it does not harm anybody I guess get the corresponding xml file contents (xml) for the narration it could be protected (Authorized), but I guess I would have problems with available client components support, so I preferred it to be anonymous, as it does not harm anybody I guess get the corresponding xml file for the recitation it could be protected (Authorized), but I guess I would have problems with available client components support, so I preferred it to be anonymous, as it does not harm anybody I guess Gets Verse Sync Information narration id updates narration metadata reviewstatus cannot be set to Approved or Rejected using this method, use moderate method instead only these set of fields are updatable: AudioTitle, AudioArtist, AudioArtistUrl, AudioSrc, AudioSrcUrl, ReviewStatus (Draft to Pending and vice versa and Approved/Rejected to Pending) only narrator or a moderator can update the narration Moderate pending narration (for moderating other users' recitations you also need recitation:moderate permission) Delete a recitation Replace the synchronization (xml) file of an already approved recitation belonging to the logged on user, without touching its mp3 file. Send the new xml as the single file in the request's form-data. The uploaded xml is only accepted if its embedded PoemId and audio checksum match the target recitation exactly (i.e. it must be a resync of the very same mp3 already on record) - this endpoint never accepts a new mp3. On acceptance, the xml is queued for re-publishing to the external FTP server(s) in the background and you receive a notification with the final result (success or failure). recitation id Get User Uploads default: false, user must have recitation::moderate permission to be able to see all users uploads additional headers: paging-headers, audio-upload-enabled upload, update, moderate and delete operations on recitations might temporarily become disabled, this method gets the current status remarks: the value of this flag is provided as a custom header called audio-upload-enabled in some common GET methods in order to reduce the need for a separate query Reciation Upload if you send true to replace parameter, if there is an existing recitation for the poem from the user with the same Audio Artist name corresponding mp3+xml files are replaced an no other changes is applied (no new post, preserving recitation position) Get User Profiles Get User Default Profile Add a narration profile Update a narration profile Delete a narration profile publishing tracker data Transfer Recitations Ownership (for recitations owned by current user) number of transfered items Synchronization Queue report an error in a recitation get errors reported for recitations reject a reported error for recitations and notify the reporter (and deletes the report) accepts a reported error for recitations, change status of the recitation to rejected and notify the reporter and recitation owner (and deletes the report) accepts a reported error for recitations, add mistake to approve the mistake and notify the reporter and recitation owner (and deletes the report) edit approved mistake of a recitation text remove approved mistake up vote a recitation revoke recitaion up vote revoke recitaion up vote get user upvoted recitations compute poem recitations order (no update) starts checking recitaions with missing files and add them to reported errors list job retry publish unpublished narrations Makes recitations of فریدون فرح‌اندوز first recitations get category top one recitations check if a category has any recitations rss for category top one recitations readonly mode constructor Artifact Service IUserPermissionChecker instance IAppUserService instance Configuration options constructor Generic Image Provider constructor Long Running Jobs constructor roles constructor Semantic ("find a poem about...") search — deliberately its own controller, not a method on GanjoorController, after a production incident: GanjoorController's constructor took ISemanticSearchService (indirectly requiring EmbeddingIndex/QueryEmbedder to load successfully), so a resource-loading failure prevented the ENTIRE controller from being constructed — a 503 on every endpoint under /api/ganjoor, not just this feature. Same route prefix as before (api/ganjoor), so the endpoint's URL is unchanged (POST /api/ganjoor/search/semantic) — only which controller class hosts it changed. A future failure in this feature's own dependencies can now only ever affect this one controller/endpoint, never GanjoorController or anything else. semantic ("find a poem about...") search Fire-and-forget click reporting — called via navigator.sendBeacon (or a keepalive fetch as fallback) right as a result link is clicked, so it can complete even as the browser navigates away. ReportClickAsync itself is fully best-effort (never throws in a way that matters here), so this always returns 200 regardless of whether the underlying write actually succeeded — the caller isn't listening for the response either way. add site banner (send form) with these fields: alt, url and an image attachment modify site banner delete site banner get all defined site banners get a random active site banner Ganjoor Service constructor get all languages get language by id add new language update an existing language readonly mode translation service Configuration constructor user visits tracking controller get user history add a user history track GanjoorUserPrePoemVisitViewModel(LastVisit, TotalVisits) delete a user history track start or stop tracking user User Visits Tracking Service constructor Museum Database Context Picture Files Item Attributes Artifacts Items Import Jobs Tags User Bookmarks User Notes Ganjoor Links Pinterest Links Ganjoor Audio Files Upload Sessions Uploaded files User Recitation Profiles Ganjoor Poets Ganjoor Categories Ganjoor Poems Semantic search query log — see SemanticSearchQueryLog for what is (and deliberately isn't) recorded Ganjoor Verses Narration Publishing Tracker Ganjoor Pages Ganjoor Metres singers music tracks golha tracks GolhaCollection GolhaPrograms PoemMusicTracks Ganjoor Comments Ganjoor Reported Comments Ganjoor Page Snapshots Ganjoor Site Bannaers Ganjoor Health Check Errors donations expenses donation expenditures poem corrections languages for translation ganjoor bookmarks ganjoor numbering schemas ganjoor verse numbers ganjoor half centuries ganjoor cities related poems to each poem (having same rhyme letters and prosody metre) Reported User Notes recitation error reports recitation user up votes recitation approved mistakes probable metres ganjoor user history track items (stored by his or her choice) suggested spec line for poets suggested pictures for poets faq categories faq items Temporary Model contianing duplicated poems information poem sections related sections to each section (having same rhyme letters and prosody metre) section correctons Updating related sections logs PoemGeoDateTags People tags approved kinship edges between people (family tree) - see GanjoorPersonRelation approved non-family ties between people (e.g. minister-to-king) - see GanjoorPersonAffiliation pending/reviewed suggested edits to an already-approved GanjoorRelatedPerson's own fields - see GanjoorPersonEditSuggestion pending/reviewed suggested additions, changes or removals of a kinship edge between two already-approved people - see GanjoorPersonRelationEditSuggestion Books (PDF Library) Authurs Multi Volume PDF Collections PDF Books PDF Pages PDF Sources Queued FTP Uploads PDF Ganjoor Links OCR Queue Items PDF Download Queue Related Poems discover quoted q items paper sources digital sources Category Word Counts Category Word Count Summaries tajik poets tajik cats tajik poems tajik verse tajik pages ganjoor cat corrections comment reactions donation expenditures Id GanjoorDonation Id GanjoorDonation amount Ganjoor Donation (based on donation records saved in old html format of https://ganjoor.net/donate/) id date: (donation dates have been collected in different formats, so instead of keeping their dates in a DateTime field I ought to use a string field) record date donation amount (0 for old imported records) Amount Unit amount string (avoiding to parse different formats of old html text) donor name donor link (unused) remaining (it could be ignored later when our data has been normalized enough and be calculated using related data) expenditure desc record is imported from old HTML text of donation page Ganjoor Expenses Id date amount unit description donation expenitures Ganjoor Donation View Model id - null for POST api - record date donation amount (0 for old imported records) "تومان" is good, Amount Unit, if you send another Unit it would be consumed with EXPENSES of the same Unit, if it send EMPTY it wotld not be consumed! donor name null for POST api - date: (donation dates have been collected in different formats, so instead of keeping their dates in a DateTime field I ought to use a string field) null for POST api -amount string (avoiding to parse different formats of old html text) null for POST api -remaining (it could be ignored later when our data has been normalized enough and be calculated using related data) null for POST api -expenditure desc null for POST api - record is imported from old HTML text of donation page update date and description view model (donation + expense limited update api) date description Items or pictures status Id parent id Order in Collections Friendly Url Name Name In English Description Description In English Main Image Last Modified for caching purposes All Images Attributes Museum Master Item Id Friendly Url Publish Status Name Name In English Description Description In English Date/Time Last Modified for caching purposes Cover Item Index Cover Image Cover Image Id Parts of this item Item Count (for lists and queries) Attributes to view model Artifact Picture Files Title Title in English Description Description in English Publish status order in the collection it belongs to it نام فایل ذخیره شده با بالاترین کیفیت e0d7050a-7e30-4a2f-b181-0daa4a1e1068.jpg نام فایل ذخیره شده با بالاترین کیفیت fbe9cc8e-12fa-4cb9-8f09-bbf353333383.jpg عرض تصویر مناسب نمایش در صفحهٔ اختصاصی طول تصویر مناسب نمایش در صفحهٔ اختصاصی عرض تصویر با اندازه مناسب نمایش در صفحات لیستی طول تصویر با اندازه مناسب نمایش در صفحات لیستی source url Last Modified for caching purposes url to access this image from THE external host, contains '/norm/' which when you replace it with '/thumb/' you would have ExternalThumbnailImageUrl and if you replace it with '/orig/' you would have a url for ExternalOriginalSizeImageUrl which MIGHT NOT EXIST and end in a 404 error duplicated a picture record (Id is missing so you should store this to get a new Id) Item Attribute Id Order tag type Friendly Url Publish Status Name Name In English Plural Name Plural Name In English Value is normally changed globally نوع ویژگی عادی پیوند جستجو پیوند و جستجو متن چپ به راست بدون مقدار عنوان در فهرست IIIF لینک در نسخه - برای نمایش انجامه‌ها Attribute Value Id Friendly Url Publish Status Value Values In English link or .... new artifact view model RArtifactItemRecord with additional info main item info parent parent name parent image parent image external url parent item count empty or null means this is last item next image next image external url empty or null means this is first item prev image prev image external url Formatted Tags Museum Master Item View Model Id Friendly Url Publish Status Name Name In English Description Description In English Date/Time Last Modified for caching purposes Cover Item Index Cover Image Cover Image Id Parts of this item Item Count (for lists and queries) Attributes Tags Binary Tagged Items Titles of Items in Contents Artifact Tag (RTag with grouped related RTagValues of Artifact) Id Order tag type Friendly Url Publish Status Name Name In English Plural Name Plural Name In English Value is normally changed globally Values Tag Bundle View Model Item Title Item Friendly Url Items Count Item Image Id url to access the image from THE external host, contains '/norm/' which when you replace it with '/thumb/' you would have ExternalThumbnailImageUrl and if you replace it with '/orig/' you would have a url for ExternalOriginalSizeImageUrl which MIGHT NOT EXIST and end in a 404 error Tag Bundle View Model Id Friendly Url Name Plural Name Values Artifact Binary Tagged items Summation Tag Name Tag Friendly Url Item Count Title of an Item in Artifact Contents Title Order Level Item Friendly Url specific forms and permission artifact tag note view drafts edittag awaiting publish import moderate review suggested ganjoor links audio narrations ganjoor contents FAQ contents reorder review suggested songs add song from any source manage footer bannaers donations translations photos pdf ftp list of forms and their permissions ganjoor user public profile Id user image nick name biography web site LoginViewModel with app specfic properties keep user browsing history if he or she wants so Bookmark Type Private Bookmark Public Favorite Private Note (GanjoorComment) User Bookmarks Id User Id User Master Record Id Master Record Item Record Id Item Record DateTime Type User Note Id User Id Master Record Item Record DateTime Type User Note Queued FTP Upload id local file path remote file path delete file after upload queue date processing process date error FAQ Category Id title order description published category items FAQ Item id question excerpt answer pinned item order category id category item order in category content search hashtag 1 hashtag 2 hashtag 3 hashtag 4 published Audio Review Status All / Unfiltered (for queris) Draft pending for review approved Rejected after initial review Rejected due to errors Audio Synchronization Status (binary combination is acceptable) no changed new upload meta data should be updated sound files should be reuploaded (this means xml file is also needs to reuploaded) Item should be deleted Poem Narration Id Owner User Owner User Id Final data is actually exported to a MySQL database which this auto increment field is its key Ganjoor Post Id Caution: this could become a relation with newly added table but if we do this we lose the ability to drop and create without losing narration data This determines where an audio is displayed between a list of sounds related to a specfic poem Using this field content you would determine xml, mp3 and ogg file names combining this with + .ext would preduce relative path of sound files on our file server the full path would become [HOMEPath] + '/i/' + this value "a2" We had previously used different pathes because when a directory became overcrowded our connection time would increase unbearably due to IRAN internet problems, so it is sensible to store it per record and not as a global option value MP3 File Path MP3 url This is also a legacy field "/a2/x" XML Schema is defined based on Desktop Ganjoor (https://github.com/ganjoor/ganjoor) code, for more information take a look at this code: https://github.com/ganjoor/ganjoor/blob/master/ganjoor/Audio%20Support/PoemAudioListProcessor.cs XML url Audio Title Audio Artist Audio Artist Url Audio Source Audio Src Url Legacy Audio Guid Audio File CheckSum mp3 size in bytes ogg size in bytes Upload Date File Last Updated Review Date (Approve or Reject) MP3 File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) XML File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) AudioSyncStatus Review Status Reviewer user Reviewer user Review Message initial score for ordering recitations Recitation Type in sync with text Recitation approved mistake id recitation id recitation mistake number of verses affected couplet index Error Report for recitations Id Recitation Id Recitation Reason Text Reporter User Id Reporter User DateTime number of verses affected couplet index Narration Publishing Tracker Id Poem Narration Id Poem Narration Start Date XML File Copied MP3 File Copied First MySql DB Updated Second DB Updated Finished Finish Date Last Excecption recitation type normal recitations commantaries unfiltered recitation user up vote (the records would be deleted manually if the owner user account gets deleted) Id Recitation Id Recitation User Id, defining it this way causes no cascade relation User date time Narration Profiles for users Id User UserId Profile Name this would be appended to audio files names prefixed by a dash to make them unique and specfic to user filenames usually would look like {GanjoorPostId}-{FileSuffixWithoutDash}.{ext} for example 2200-hrm.xml artist name artist url audio src audio src url is default Moderation Result no moderation approve reject Public (no user specific data) for publish recitations Audio Id Ganjoor Post Id Poem Full Title Poem Full Url (without domain name) Audio Title Audio Artist Audio Artist Url Audio Source Audio Src Url Legacy Audio Guid Audio File CheckSum mp3 size in bytes Publish Date File Last Updated Mp3 Url Xml Text, xml file url is {WebServiceUrl.Url}/api/audio/file/{audio.Id}.xml Poem Plain Text Poem Html Text mistakes This determines where an audio is displayed between a list of sounds related to a specfic poem Recitation Type in sync with text upvoted by current user (filled at client) Post Error Report for recitations Id Recitation Id Reason Text recitation number of verses affected couplet index DateTime approved mistake view model id mistake number of verses affected couplet index Poem Narration Moderation View Model Moderation Result Rejection Message Recitation Ordering View Model recitation earlyness advatage initial score upvotes from users other than the owner approved mistaked total scores computed order recitation (it is null by default and you should fill it using a separate api call based on RecitationId) RecitationPublishingTracker View Model user email Poem Full Title Artist Name Operation In Progress XML File Copied MP3 File Copied First MySql DB Updated Second DB Updated Succeeded Error Exception Start Date Finish Date Verse Sync Range Verse Order Verse Text Audio Start in Milliseconds Poem Narration view model parameterless constructor for deserialization support constructor Id Owner User Final data is actually exported to a MySQL database which this auto increment field is its key Ganjoor Post Id Poem Full Title Poem Full Url (without domain name) This determines where an audio is displayed between a list of sounds related to a specfic poem Using this field content you would determine xml, mp3 and ogg file names combining this with + .ext would preduce relative path of sound files on our file server the full path would become [HOMEPath] + '/i/' + this value "a2" We had previously used different pathes because when a directory became overcrowded our connection time would increase unbearably due to IRAN internet problems, so it is sensible to store it per record and not as a global option value MP3 File Path MP3 url OGG File Path OGG url This is also a legacy field "/i/a2/x" XML Schema is defined based on Desktop Ganjoor (https://github.com/ganjoor/ganjoor) code, for more information take a look at this code: https://github.com/ganjoor/ganjoor/blob/master/ganjoor/Audio%20Support/PoemAudioListProcessor.cs XML url Audio Title Audio Artist Audio Artist Url Audio Source Audio Src Url Legacy Audio Guid Audio File CheckSum mp3 size in bytes ogg size in bytes Upload Date File Last Updated Review Date (Approve or Reject) MP3 File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) XML File local path on Windows Server (if item is not rejected probably it is not valid and it is deleted) Value is one or a combination of Review Status Review Message Recitation Type in sync with text UserNarrationProfile View Model Id Do not fill it in POST api User Do not fill it in POST api UserId Do not fill it in POST api Profile Name this would be appended to audio files names prefixed by a dash to make them unique and specfic to user filenames usually would look like {GanjoorPostId}-{FileSuffixWithoutDash}.{ext} for example 2200-hrm.xml update [4/10/2021]: you can send this one empty artist name artist url audio src audio src url is default ganjoor link for artifacts and items Id Ganjoor Post Id ganjoor url ganjoor title Artifact Id artifact Artifact Item Id Artifact Item User Id who suggested the link Suggestion Date User who suggested the link User id who reviewed the link User who reviewed the link Review Date review result Synchronized with ganjoor display this image on poem page original source url link to original source is text original source Ganjoor Paper Source Id poet id cat id cat full title cat full url book type book full url naskban book id book title description is the text original source for the category? cover thumbnail image url match percent reviewed by a human position in sorting Link Type Custom Museum Pinterest Instagram Naskban PDFBook Ganjoor Link Id Ganjoor Post Id ganjoor url ganjoor title pdf book id page number User Id who suggested the link Suggestion Date User who suggested the link User id who reviewed the link User who reviewed the link Review Date review result Synchronized with ganjoor is the is the text original source? PDF Page Title external thumbnail image url suggested using machine computations pinterest link Id Ganjoor Post Id ganjoor url ganjoor title alt text link type pinterest url pinterest image url User Id who suggested the link (this would always be null for anonyous suggestions, kept it so that someday it would be used to integerate GanjoorLink into this class) Suggestion Date User who suggested the link User id who reviewed the link User who reviewed the link Review Date review result review description Artifact Id artifact Artifact Item Id Artifact Item Synchronized with ganjoor is the is the text original source? pdf book id page number naskban link id match percent reviewed by a human Review Result Awaiting (default) Approved Rejected Ganjoor Link View Model Id Ganjoor Post Id ganjoor url ganjoor title entity name entity friendly url entity image id review result Synchronized with ganjoor suggested by is text original source url to access this image from THE external host, contains '/norm/' which when you replace it with '/thumb/' you would have ExternalThumbnailImageUrl and if you replace it with '/orig/' you would have a url for ExternalOriginalSizeImageUrl which MIGHT NOT EXIST and end in a 404 error Link Suggestion Ganjoor Post Id ganjoor url ganjoor title Artifact Friendly Url Artifact Item Id Safe pinterest link view model Id Ganjoor Post Id ganjoor url ganjoor title alt text link type pinterest url pinterest image url Suggestion Date User id who reviewed the link Review Date review result review description Artifact Id Artifact Item Id Synchronized with ganjoor entity name entity friendly url entity image id pinterest / ganjoor link suggestion view model Ganjoor Post Id ganjoor url ganjoor title alt text link type pinterest url pinterest image url id CatId Word Count row number in category id CatId unique word count total word count correction review result id source url slug short name full name source category couplets count discover quoted q item id poet id poem id related poet id related poem id id poem id poet id poem order poet name poet image url poem full url poem full title excerpt other poems id poem id poem section index poet id order poet name poet image url poem full url poem full title excerpt target poem id target section index other poems Ganjoor Category id poet_id poet text parent_id parent url => slug sample: /hafez/ghazal TOC Style Category Type additional description or note html mode of additional description or note order when mixed with poems published category book name poet image user image id sum up sub categories geo locations category map name correct id category id category additional description or note html mode of additional description or note additional description or note html mode of additional description or note note date user Id user reviewed reviewdate review note reviewer id reviwer user id application order for category review result for description had any effect on category after moderation? effective in history hide the editors name page id Ganjoor Category Type half century id name order start end show in time line poets half century id order poet id poet Ganjoor Comment Id Ganjoor Poem Id Ganjoor Poem Id user id user author name (MySql imported field) author email (MySql imported field) author url (MySql imported field) author IP address (MySql imported field) comment date comment In Reply to Other Comment Reference Comment Id publish status related verse 1 Id, this is always null, because verses are removed and created on operations like editing and their Ids changed, so later I decided to rely on (PoemId + CoupletIndex) instead do not use related verse 2 Id, this is always null, because verses are removed and created on operations like editing and their Ids changed, so later I decided to rely on (PoemId + CoupletIndex) instead do not use couplet index Total likes Total dislikes Internal ranking key for sorting. Not shown to users. Comment Abuse Report id comment id comment reason code for better grouping: offensive, bogus, other some explanotory text provided by reporter reported by id reported by user +1 = Like -1 = Dislike how to sort a poem's comments highest rating first (ties broken by oldest first) oldest comment first newest comment first Temporary Model contianing duplicated poems information Id source category id (this category is about to be deleted and redirected) source poem id source poem destination poem id destination poem Geo Locations (Cities) referred by Ganjoor Metadata id name Latitude Longitude AI generated Ganjoor Page Health Check log id referer url broken link multiple targets for a page target url Language or system of writing for translating poems id name code native name is right to left description language from code Poem prosodic Metre Id Url Slug Rythm مفاعیلن مفاعیلن فعولن Name هزج مسدس محذوف Description Total Verse Count (its actually couplet count) Numbering Schema id name starting category id ending category ending category id ending category lines count verse count total poem couplets total paragraphs last counting date Ganjoor Page id Page Type Published page order title cat + parent cats title + title url => slug sample: /hafez/ghazal/sh1 Html Text parent id parent related poet id related poet related category id related category related poem id related poem second poet id second poet post date no index (search engines are blocked to index the page) if a page url is changed, store the old URL here to be redirected automatically snapshot of ganjoor pages to keep track of their changes id ganjoor page id ganjoor page id - this record is then modified by this user and made obsolete this record is then modified by this user and made obsolete record date a description of the modfication title url => slug Html Text Poem Rhythm rhyme letters source name source url slug old collection or book name for Saadi's ghazalyiat (طیبات، خواتیم و ....) old collection page url e.g /saadi/tayyebat Ganjoor Page Type None Poet Page Cat Page Poem Page Prosody Similars Hashieha Page Similar Poems of two poets Prosody and Statistics an approved non-family tie between two people (GanjoorRelatedPerson rows) - e.g. a minister serving a king. Kept separate from GanjoorPersonRelation (kinship) rather than folded into it: this is what lets two unrelated family trees show up as adjacent to each other (e.g. browsing the Barmakid tree surfaces a link out to the Abbasid tree via a shared minister) without treating "family tree" as its own entity to be linked - the tie is between the two people, and the tree-to-tree adjacency is just what falls out of rendering it that way. record id first person in the tie - for a directional type (Minister, Advisor, Courtier, Patron) this is the one in the subordinate/serving role; for a symmetric one (Ally, Rival) order doesn't matter first person (navigation) second person in the tie - for a directional type this is the one being served second person (navigation) the kind of tie between Person1 and Person2 free-text note (e.g. sourcing/reasoning, or what the tie actually is when AffiliationType is Other) a contributor's suggested edit to an existing, already-approved GanjoorRelatedPerson's own fields (name, bio, birth/death info, family tree caption, ...). Reviewed/approved the same way a GanjoorPoemGeoDateTagCorrection is - see GanjoorService-ModeratePersonEditSuggestion.cs - but standalone: it targets a person directly, not a poem/couplet. There is no admin-only direct-edit path for GanjoorRelatedPerson; this suggestion queue is the only way any of its fields change after it is first created. suggestion id the person this suggestion proposes changes for the person this suggestion proposes changes for (their CURRENT, still-unchanged fields - for showing a before/after diff to the moderator) date suggester's user id suggester suggested name suggested description suggested wikipedia url suggested birth year in lunar hijri suggested death year in lunar hijri suggested ValidBirthDate suggested ValidDeathDate suggested birth location id suggested birth location suggested death location id suggested death location suggested family tree caption true if this suggestion is actually a request to delete the person outright, not to change their fields - when true, every Suggested* field above is ignored on approval (they still carry whatever the contributor's pre-filled form happened to hold) and ModeratePersonEditSuggestionAsync removes the person and every relation/affiliation/tag reference to them instead. Kept on this same entity rather than a separate one so the existing suggest/review UI and permissions are reused as-is. suggester's note to the moderator reviewed review result review date review note reviewer's user id reviewer an approved kinship edge between two people (GanjoorRelatedPerson rows) - the live/materialized counterpart of a pending suggestion, which travels as JSON on GanjoorPoemGeoDateTagCorrection's SuggestedPersonGraphJson until it's approved and turned into rows here. The graph as a whole (all rows in this table) is a general kinship graph, not a strict tree - see PersonRelationType. record id first person in the relation - for a directional relation type (Parent, Ancestor) this is the parent/ancestor side; for a symmetric one (Sibling, Spouse) order doesn't matter first person (navigation) second person in the relation - for a directional relation type (Parent, Ancestor) this is the child/descendant side second person (navigation) the kind of relation between Person1 and Person2 for RelationType == Ancestor, an optional known exact degree (e.g. 2 for "grandparent", 3 for "great-grandparent") - left null when only the relative order is known, not the exact number of generations in between. Not used for other relation types. free-text note (e.g. sourcing/reasoning for this relation) a contributor's suggestion to add a brand new kinship edge between two already-approved people, change an existing edge's type/degree/note, or remove an existing edge outright - the moderated counterpart of GanjoorPersonEditSuggestion, but for the edges between people rather than one person's own fields. Reviewed the same way (see GanjoorRelatedPersonService.ModeratePersonRelationEditSuggestionAsync). suggestion id what this suggestion proposes doing whether this suggestion is about a kinship edge (Family, using SuggestedRelationType / ExistingRelationId / ExistingRelation) or a non-family affiliation edge (Affiliation, using SuggestedAffiliationType / ExistingAffiliationId / ExistingAffiliation instead). Defaults to Family so rows created before this field existed keep working unchanged. for Modify/Remove when Kind is Family: the existing GanjoorPersonRelation row this suggestion targets. Null for Add, where there's nothing existing yet, and for any Kind == Affiliation suggestion (see ExistingAffiliationId instead). the existing relation being modified/removed (for showing a before/after diff to the moderator) - null for Add, and for Kind == Affiliation for Modify/Remove when Kind is Affiliation: the existing GanjoorPersonAffiliation row this suggestion targets. Null for Add, and for any Kind == Family suggestion (see ExistingRelationId instead). the existing affiliation being modified/removed (for showing a before/after diff to the moderator) - null unless Kind is Affiliation and Action is Modify/Remove the pair this suggestion is about. For Add, this and Person2Id define the new relation. For Modify/Remove, these are copied from ExistingRelation at submission time, so the suggestion always accurately reflects what it targets even if the underlying relation changes again before this is reviewed. first person (navigation) second person id, see Person1Id second person (navigation) for Add/Modify when Kind is Family: the relation type to create/change to. For Remove: a snapshot of the existing relation's type at submission time, purely for display (removing doesn't use it). Ignored when Kind is Affiliation - see SuggestedAffiliationType instead. for Add/Modify when Kind is Affiliation: the affiliation type to create/change to. For Remove: a snapshot of the existing affiliation's type at submission time, purely for display. Null/ignored when Kind is Family - see SuggestedRelationType instead. see GanjoorPersonRelation.DegreeHint - same for/Add/Modify/Remove-display purpose as SuggestedRelationType above see GanjoorPersonRelation.Note - same Add/Modify/Remove-display purpose as SuggestedRelationType above suggester's note to the moderator (e.g. why an existing relation is wrong) date suggester's user id suggester reviewed review result review date review note reviewer's user id reviewer Ganjoor Poem id cat-id cat title cat + parent cats title + title url => slug sample: /hafez/ghazal/sh1 verses text verses text as html (ganjoor.net format) prosody information in fact this should be a many-to-many relationship, but our current dataset lacks such a relationship instance and because in fact it is actually this relationship should exists between a non-existant entity called block of poem I ignored this relationship to take care of it whenever the block entity would be added to the data structure metre rhyme letters source name source url slug old collection or book name for Saadi's ghazalyiat (طیبات، خواتیم و ....) old collection page url e.g /saadi/tayyebat order when mixed with categories published language (this is intended to affect html page encodings and not determine actuallly accents and ....) poem summary poem is claimed by multiple poets poem correction Correction Id poem Id poem modified verses suggested geo/date tags title original title rhythm original rhythm note date user Id user reviewed reviewdate review note reviewer id reviwer user id application order for poem review result for title review result for rhythm had any effect on poem after moderation? effective in history rhythm 2 original rhythm 2 review result for rhythm 2 rhythm 3 original rhythm 3 review result for rhythm 3 rhythm 4 original rhythm 4 review result for rhythm 4 rhyme letters original rhyme letters rhyme letters review result language original language language review resukt suggested poem summary original poem summary summary review result hide the editors name poem format original poem format poem format review result poem format unknown غزل قصیده مثنوی قطعه رباعی دوبیتی غزل/قصیده/قطعه ترکیب بند تک بیت نیمایی ترکیب بند مسمط مثلث مستزاد رباعی مستزاد مسمط مربع مسمط مخمس سپید نیمایی یا سپید مسمط مسدس مسمط مثمن مسمط چهارپاره چند بندی بحر طویل suggested geo/date tag for a poem, pending moderation as part of a GanjoorPoemCorrection record id couplet index (matches PoemGeoDateTag.CoupletIndex) an existing, already approved location - set this OR the Suggested* fields below, not both existing location (navigation) name for a brand new, not yet approved location suggestion latitude for a brand new, not yet approved location suggestion longitude for a brand new, not yet approved location suggestion lunar year (Hijri) lunar month (Hijri) lunar day (Hijri) related person id - an existing, already approved GanjoorRelatedPerson. Set this OR SuggestedPersonGraphJson below, not both (same pattern as LocationId/Suggested* above). related person (navigation) a brand new, not yet approved person (and optionally that person's relatives/relations, which may themselves be new people) - serialized JSON rather than its own set of correction tables, because a single suggestion can introduce several interlinked new people at once (e.g. "add this person, and their father, and the relation between them") and a new person referencing another not-yet-existing new person has no real id to point at until the whole graph is approved together. Expected shape (local keys are only used to resolve relations within this same submission and never stored beyond approval time): { "person": { "localKey": "p1", "existingPersonId": null, "name": "...", "description": "...", "wikiUrl": "...", "birthYearInLHijri": null, "deathYearInLHijri": null, "validBirthDate": false, "validDeathDate": false, "birthLocationId": null, "deathLocationId": null, "familyTreeCaption": null }, "relatedPeople": [ { "localKey": "p2", "existingPersonId": 42, ... } ], "relations": [ { "kind": "family", "person1": "p1", "person2": "p2", "relationType": "Parent", "degreeHint": null, "note": "..." }, { "kind": "affiliation", "person1": "p2", "person2": "p3", "affiliationType": "Minister", "note": "..." } ] } "person" is the node that ends up assigned to PersonId once approved. Only set when PersonId above is null. Each entry in "relations" carries a "kind" discriminator so one submission can suggest both kinship edges (materialized as GanjoorPersonRelation, "relationType" against PersonRelationType) and non-family ties (materialized as GanjoorPersonAffiliation, "affiliationType" against PersonAffiliationType) at once - e.g. introducing a person along with both their father and the king they served. if true, this tag is excluded from category/poet-level map aggregation (e.g. a place mentioned only for comparison, not actually visited/relevant to the poet's own path) - default false, matching PoemGeoDateTag.IgnoreInCategory contributor's own reasoning for this tag - especially important for indirect date references (e.g. an abjad-encoded year, or an allusion to a known historical event) where the connection to the suggested location/date isn't self-evident from the tag alone true if this suggestion is for removing an existing PoemGeoDateTag rather than adding a new one id of the existing PoemGeoDateTag this suggestion would remove, when MarkForDelete is true review result reviewer's note Probable Prosody Information for a poem Id poem id poem metre section id Ganjoor Poem Section record id Poem Id Poem poet id might be different from GanjoorPoem.PoetId poet might be different from GanjoorPoem.PoetId use this field instead of Id for referencing to ease record deletion for verses, Index starts at 0, each poem should have at least one non-virtual part ordered by Index without break for lots of poems Number equals to Index + 1, but for rare ones such as a paragraph containing multi-band poem this could be different, 0 means no visible numbers section type how GanjoorPoemSection is linked to GanjoorVerse prosody information in fact this should be a many-to-many relationship, but our current dataset lacks such a relationship instance metre for non WholePoem sections, there must be a reference section for meters in order to make them updatable rhyme letters verses text verses text as html (ganjoor.net format) valid for whole poem sections first couplet index language, null means farsi couplets count top 6 related sections old metre id to see it needs refreshing related sections old rhyme letters to see it needs refreshing related sections modified excerpt poem section correction Correction Id section id section rhythm original rhythm review result for rhythm rhythm 2 original rhythm 2 review result for rhythm2 break from verse1 VOrder review result for break from verse1 VOrder break from verse2 VOrder review result for break from verse2 VOrder break from verse3 VOrder review result for break from verse3 VOrder break from verse4 VOrder review result for break from verse4 VOrder note date user Id user reviewed reviewdate review note reviewer id reviwer user id application order for poem had any effect on poem after moderation? effective in history break from verse5 VOrder review result for break from verse5 VOrder break from verse6 VOrder review result for break from verse6 VOrder break from verse7 VOrder review result for break from verse7 VOrder break from verse8 VOrder review result for break from verse8 VOrder break from verse9 VOrder review result for break from verse9 VOrder break from verse10 VOrder review result for break from verse10 VOrder rhyme letters original rhyme letters rhyme letters review result language original language language review result poem format original poem format poem format review result hide the editors name poem translation id language id language poem id poem title translation verses published user id user date/time comments Ganjoor Poet cat_id field is removed, it is retrievable by querying where == and == null id name description short name poet image user image id published on website birth year in lunar hijri death year in lunar hijri Home page pin order (zero means not pinned) BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date birth location id birth location death location id death location Ganjoor Poet Picture id Poet Id Poet order picture id picture suggested by id suggested by published selected publication date Ganjoor Poet Specification Id Poet Id Poet order Contents published suggested by id suggested by publication date related poem to a poem record id poem id poem related poem id (this could be a poem we do not have it available) this poem is written and should be displayed prior to the related poem if the related poet poem has multiple poems related to this poem, which one is chosen to be shown at main poems pages? this sort order can be used to pin a record on top related poem peot death date in hijri (used for sorting) related poem poet name related poem poet url related poem poet image full title full url couplet 1 verse 1 is main part couplet 1 verse 2 is main part couplet 1 index related couplet 1 verse 1 is main part related couplet 1 verse 2 is main part related couplet 1 index description published (approved) when a poem contains more than one quoted couplet from another one this should be more than 1 poems (same poem) are claimed by both poets poet id (redundant for simplifying queries) related poet id related indirectly Suggested by user id Suggested by user suggestion date review date reviewer id reviwer user id instead of deleting rejected quotes keep them for user (not published + not rejected means not reviewed) review note a person refered by a poem id name description wikipedia url birth year in lunar hijri death year in lunar hijri BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date birth location id birth location death location id death location AI generated optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set). ganjoor.net site banner id banner image image id alternate text target url active id tajik title additional description or note in Tajik poet_id poet id verses text as html (ganjoor.net format) id cat id title verses text sample: /hafez/ghazal/sh1 cat + parent cats title + title cat id tajik nick name additional description or note in Tajik birth year in lunar hijri id poem_id vorder tajik text ganjoor TOC options Ganjoor Bookmark Id User Id User Poem Id Poem couplet index Verse Id, this is always null, because verses are removed and created on operations like editing and their Ids changed, so later I decided to rely on (PoemId + CoupletIndex) instead Verse 2 Id, this is always null, because verses are removed and created on operations like editing and their Ids changed, so later I decided to rely on (PoemId + CoupletIndex) instead DateTime private note for bookmark ganjoor user history track item (stored by his or her choice) id user id (nullable to prevent a db cascade relation) user poem id poem date/time counter of visits (1 for first and ...) Ganjoor Verse global id, auto generated (missing in Ganjoor Desktop database) poem_id poem vorder position text couplet index GanjoorPoemSection index second GanjoorPoemSection index third GanjoorPoemSection index forth GanjoorPoemSection index language id language couplet summary id Numbering schema Id Numbering schema Poem Id (no relation is defined) verse couplet index (do not add related verses here) line number is poem verse or paragraph number based on type of line: is it a poem verse or a paragraph Verse translation id verse id verse translated text Verse Vorder / Text record id verse order text original text review result note couplet indexs verse position original verse position verse position result mark for delete mark for delete result new verse, if it is a new verse you should insert it in VORder position and shift verses previously positioned at VORder and after that new verse result language id original language id language review result suggested couplet summary original couplet summary summary review result the kind of non-family tie a GanjoorPersonAffiliation represents between two people - e.g. a minister serving a king, an advisor, a patron. Unlike PersonRelationType this has nothing to do with kinship; it's what lets two otherwise unrelated family trees show up as adjacent (e.g. "some Barmakids served the Abbasid court") without merging them into one tree. New values can be appended safely later (stored as int) as more cases come up. Person1 served as minister/vizier to Person2 Person1 was an advisor/counselor to Person2, without holding a formal ministerial post Person1 was a courtier/attendant/servant of Person2 (a catch-all for court-affiliated roles not covered by a more specific type) Person1 was a patron/sponsor of Person2 (e.g. a king patronizing a poet) Person1 and Person2 were allies (symmetric - order doesn't matter) Person1 and Person2 were rivals/enemies (symmetric - order doesn't matter) Person1 was a servant (personal attendant, not a court office) of Person2 - narrower than Courtier, for a purely domestic/personal-service tie rather than a court role Person1 and Person2 were companions/comrades - e.g. fellow travelers, brothers-in-arms (symmetric - order doesn't matter) Person1 succeeded Person2 in a role/office/throne (directional - Person1 is the one who came after) Person1 (a poet) wrote panegyric/praise poetry (قصیدهٔ مدح) about Person2 (the ممدوح - the praised patron/king/dignitary) - directional, same convention as Minister/Patron Person1 (a poet) wrote satirical/mocking poetry (هجو) about Person2 (the هجو شده - the satire's target) - directional, same convention as Minister/Patron doesn't fit any of the above - rely on Note for what the tie actually is what a GanjoorPersonRelationEditSuggestion proposes doing to the kinship graph create a brand new GanjoorPersonRelation between Person1Id and Person2Id change the RelationType/DegreeHint/Note of the existing relation ExistingRelationId points to remove the existing relation ExistingRelationId points to outright which kind of edge a GanjoorPersonRelationEditSuggestion is about: a kinship tie (GanjoorPersonRelation, via SuggestedRelationType/ExistingRelationId) or a non-family affiliation tie (GanjoorPersonAffiliation, via SuggestedAffiliationType/ExistingAffiliationId). Defaults to Family (0), so every suggestion row created before this field existed keeps being read exactly as it was before - a plain kinship suggestion. the kind of kinship edge a GanjoorPersonRelation represents between two GanjoorRelatedPerson rows. The graph is not a strict tree - siblings can be known without their parents, an ancestor can be known without the exact number of generations in between, etc. - so this is a small, deliberately open set rather than a rigid parent/child-only model. Person1 is a parent of Person2 (directional - the mirror "child" relation is implied, not stored as a second row) Person1 and Person2 are siblings (symmetric - order doesn't matter). Doesn't require either parent to be known/recorded. Person1 and Person2 are spouses (symmetric - order doesn't matter) Person1 is a known ancestor of Person2 (directional) but the exact number of generations between them is not known/recorded - see GanjoorPersonRelation.DegreeHint for the case where the exact degree (e.g. "grandparent") IS known Geo + date tags for poems Id Poem Id Poem couplet index location geo location Hijri Ghamari year Hijri Ghamari month Hijri Ghamari day sample: 14440101, would be used in sorting events verified date ignore in category related person id related person AI generated optional explanatory note for this tag (e.g. why this couplet is relevant to the tagged person - carried over from the suggestion's SuggestionNote when a moderator approves it, and editable afterwards like the tag's other fields) the tagged couplet's own text (both مصرع of the verse at PoemId/CoupletIndex), filled in on read by whichever service method needs to show it (e.g. GanjoorRelatedPersonService.GetPoemsByPersonAsync) - not a mapped database column poem related track id poem id poem track type artist name artist url album name album url track name track url GanjoorTrack Id singer singer id GanjoorTrack GolhaTrack Id GolhaTrack description approved Suggested by user id Suggested by user approval user would be extractable from AuditLogs approval date broken link instead of deleting rejected songs keep track of them rejection cause Song Order poem music track type all https://beeptunes.com/or https://khosousi.com http://www.golha.co.uk itunes.com https://open.spotify.com/ user supplied link to any platform accepted by RMuseum.Utils.MusicUrlValidator poem section type sections like paragraphs or poems within paragraphs of text bands of a multi-band poem band couplets (virtual) some are virtual because they contain verse from different parts of poems (like verses from all band couplets) couplets (virtual) for Masnavi where to read the exported public data tree from, for StartImportFromPublicDataRepo true: fetch over HTTP, Location is a base URL (e.g. jsDelivr). false: read from a local folder, Location is a filesystem path (e.g. a `git clone`). base URL (UseHttp=true) or local folder path (UseHttp=false) of the exported data tree 0 imports every poet in the export; a specific poet id imports only that poet — handy on a slow connection, or when only one poet's data is needed for local testing Root manifest written at the repository root (manifest.json). Lets consuming apps detect schema changes and do cheap "did anything change" checks without downloading the whole tree. bump this whenever a DTO shape changes in a way consumers should know about UTC generation timestamp of this run (informational only — never embed per-file timestamps inside poem/cat files, that would defeat deterministic diffs) number of ids grouped into each id-index shard file (see UrlTemplates.CatIdIndexShard / PoemIdIndexShard). A consumer resolving id X fetches shard file "{X / IdIndexShardSize}.json". URL patterns for every file kind in this export, so an app can treat this repo as an API without having to read the export source code. {placeholders} are literal. poet.json — biographical data only, no account/user linkage exists on GanjoorPoet at all _cat.json — one per category/collection folder child categories, sorted by Id for deterministic output poems directly under this category, sorted by Id for deterministic output {poem-slug}.json — the poem text itself. GanjoorPoem/GanjoorVerse/GanjoorPoemSection carry no user/account reference in the source schema, so this is a straight field allowlist, not a redaction pass. AI-generated (prefixed "هوش مصنوعی:", matching PoemSummary's own convention) until a human reviews/edits it, at which point the prefix is removed — that prefix's presence/absence is itself the provenance signal, same as for PoemSummary, nothing new needed here to track it. Published specifically so a human editor's curatorial work on a couplet's interpretation — genuinely irreplaceable if the live database were ever lost, unlike the poem text itself, which exists in many other sources — isn't only ever stored in one place. metres.json — shared lookup table written once at the repo root languages.json — shared lookup table written once at the repo root Root manifest for the Tajik data export — a separate, sparser repository from the main ganjoor-data one. Tajik translation only covers a subset of poets/categories/poems, so this export only ever contains entries that actually have a Tajik counterpart in the database (GanjoorTajikPoet/Cat/Poem/Verse) — it does not mirror the full corpus the way the main export does. Ids and FullUrls match the main repo 1:1 (same underlying entity ids), so a consumer resolves structure/metre/sections for a poem by fetching the same id/path from ganjoor-data and merging verses by VOrder. where to find the structural data (metre, rhyme, sections, full verse text in Persian) this repo's ids/paths correspond to poet.json — only written for a poet with an actual GanjoorTajikPoet record. FullUrl matches the poet's path in the main ganjoor-data repo exactly. _cat.json — only written for a category with an actual GanjoorTajikCat record. ChildCats/Poems only list children that themselves have a Tajik translation — a consumer never gets a link to a file that doesn't exist. {poem-slug}.json — only written for a poem with an actual GanjoorTajikPoem record. Deliberately doesn't carry metre/rhyme/section data — GanjoorTajikPoem/Verse don't store their own copy of that either; fetch it from the same id/path in the main ganjoor-data repo. VOrder is the join key back to the corresponding verse in the main repo's poem file — no position/section/couplet data is duplicated here, matching how GanjoorTajikVerse itself stores nothing but the transliterated text and its order. "find a poem about..." request how many results to return; a sane default is applied server-side if omitted/invalid Optional explicit scope, for a future UI (e.g. a poet picker) that wants to restrict search without relying on auto-detection from the query text. Auto-detection (see SemanticSearchService.DetectQueryScope) still runs even when these are set — an explicit PoetId/CatId narrows further, it doesn't replace detection. Skips auto-detection entirely for this request — the "search globally instead" escape hatch. Needed because substring-based detection is genuinely ambiguous sometimes: "شمع و پروانه" is both a common poetic theme AND the literal title of a book by a specific poet, so a query using it as a theme could get silently locked to that one book with no way out otherwise. An explicit PoetId/CatId still applies even with this set — that's a deliberate scope the caller asked for, not something guessed. matches GanjoorVerse.VersePosition.ToString() (e.g. "Right"/"Left") - same convention already used by the main ganjoor-data export, so any existing hemistich-pairing frontend code (see mini-ganjoor's renderVerses) can be reused as-is. A short preview from the poem's actual text - the first couple of couplets, in original verse order. Not the whole poem; just enough for a result card. cosine similarity, 0..1 for these normalized vectors (in practice results cluster in a narrower band - this is a relative ranking signal, not a calibrated probability) If the query text was recognized as referring to a specific poet and/or category/book (e.g. "در کدام شعر حافظ" -> حافظ, "در کدام بخش شاهنامه" -> شاهنامه), search was restricted to that scope and these are populated so the UI can show the user what was detected ("نتایج محدود به: حافظ") rather than silently filtering. Null/null if nothing was detected (or an explicit PoetId/CatId narrowed things without any name being recognized in the free text). Id of the SemanticSearchQueryLog row written for this request, or null if logging itself failed (best-effort — see SemanticSearchService.SearchAsync — a logging failure must never fail the search itself). The UI passes this back with POST search/semantic/click if/when a result gets clicked, so the click can be correlated to the search that produced it. Reported by the UI, fire-and-forget, if and when a person actually clicks through to one of the results — see SemanticSearchQueryLog for what this updates. 1-based position of the clicked result in the list that was returned A lightweight, privacy-conscious log of semantic search usage — query text and result metadata only. Deliberately NO user identifiers: no IP address, no user agent, no session/user id. Exists so ranking decisions (the AI-summary down-ranking, a future couplet-level search, anything else) can be evaluated against real usage instead of a handful of manually spot-checked queries. Updating related sections logs id meter id rhyme letters date/time how GanjoorPoemSection is linked to GanjoorVerse معنی فیلد position در جدول verse با توجه به مقادیر این ساختار داده مشخص می شود. imported from Desktop Ganjoor source First Verse in a normal Beyt Second Verse in a normal Beyt First Verse or the only one in a Band Beyt Second Verse in a Band Beyt Free form verse Comment Non-poem paragraph Verse Position strings Verse Position Text default verse positions get verse position text Ganjoor Batch Naming Model Start with (not including required spaces) شمارهٔ remove previous pattern from start until any numbers remove set of characters other than spaces from beginning and end .- simulate naming A GanjoorCat whose CatType is Book - a minimal view model for listing/searching books (e.g. the home page's book search box), not the full category details id (GanjoorCat.Id) book name (GanjoorCat.Title) full url, sample: /hafez/ghazal poet id (GanjoorCat.PoetId) poet short name (GanjoorPoet.Nickname) correct id category id additional description or note html mode of additional description or note additional description or note html mode of additional description or note note date user Id nickname reviewed reviewdate review note review result for description hide the editors name page id cat view model id text url => slug full url TOC Style Category Type additional description or note html mode of additional description or note order when mixed with poems published category book name user image id sum up sub categories geo locations category map name Next Category without Ancestors/Children/Poems info Previous Category without Ancestors/Children/Poems info ancestors cat children poems paper sources new image GanjoorCentury View Model id name order start end show in time line poets GanjoorCommentAbuseReport View Model id reason code for better grouping: offensive, bogus, other some explanotory text provided by reporter comment Id author name (MySql imported field) author url (MySql imported field) comment date comment status User Id in reply to poem this can be used by clients verse1 id verse2 id couplet index couplet summary Total likes Total dislikes +1 = Like -1 = Dislike new comment model Poem Id comment in reply to couplet index - send null for unrelated to a couplet comment GanjoorComment Summary View Model Id author name (MySql imported field) author url (MySql imported field) comment date comment status in reply to User Id replies this can be used by clients couplet index couplet summary for client Total likes Total dislikes +1 = Like -1 = Dislike a single user's rating value for a single comment (used to merge "my rating" state into an already fetched, anonymously cacheable comment list) GanjoorComment Id +1 = Like, -1 = Dislike result of rating/unrating a comment, returned so the client can update counts in place without re-fetching the whole comment list comment id updated total likes updated total dislikes the rating value the requesting user now has for this comment (0 if cleared) Ganjoor Couplet Number View Model name number is poem verse or paragraph number based on type of line: is it a poem verse or a paragraph total couplets total poem couplets total paragraphs Ganjoor Duplicate View Model Id Source Poem Id Source Poem cat + parent cats title + title Source Poem sample: /hafez/ghazal/sh1 Source Poem First Verse destination poem id destination Poem cat + parent cats title + title destination Poem sample: /hafez/ghazal/sh1 destination poem first verse the whole connected kinship component reachable from one person, for the interactive chart at /FamilyTree/{id} (client draws the layout; this just hands over every person and every edge in that connected subgraph - see GanjoorRelatedPersonService.GetFamilyTreeAsync). Deliberately not scoped to strict descendants of RootId: an ancestor several rows up, or a spouse's own side of the family, is part of the same connected component and is included too, exactly like akhakhafrasiyab.ir's chart isn't confined to the clicked node's own subtree. the person id the chart was requested for (not necessarily the topmost node once rendered - the client walks Parent/Ancestor edges to find the actual top of each lineage in Persons) every person in the connected component, including RootId itself every kinship edge touching any person in Persons a GanjoorPersonRelation row stripped down to what the client-side layout needs (no Person1/Person2 navigation - Persons above already carries full person data) Rhyme Analysis Result rhyme the verse analysis stopped at modify page view model title url => slug Html Text Poem Rhythm Second Poem Rhythm Third Poem Rhythm rhyme letters source name source url slug old collection or book name for Saadi's ghazalyiat (طیبات، خواتیم و ....) old collection page url e.g /saadi/tayyebat no index (search engines are blocked to index the page) if a page url is changed, store the old URL here to be redirected automatically order when mixed with categories published language (this is intended to affect html page encodings and not determine actuallly accents and ....) TOC Style Category Type additional description or note html mode of additional description or note a description of the modfication Ganjoor Page View Model id Ganjoor Page Type title cat + parent cats title + title url => slug sample: /hafez/ghazal/sh1 Html Text no index (search engines are blocked to index the page) if a page url is changed, store the old URL here to be redirected automatically Poet or Cat Poem Second Poet next normal page previous normal page GanjoorPageSnapshot summary view model id record date a description of the modfication Ganjoor Page: Summary View Model id title FullUrl the whole known network of people - every GanjoorRelatedPerson that has at least one kinship edge or non-family tie, plus every one of those edges/ties - served by GET api/people/graph for the force-directed "ontology" explorer opened via the PeopleExplorer.open() modal (formerly the standalone page /PeopleGraph), the nowruzgan-style counterpart of the strict-tree /FamilyTree/{id} view. Loaded whole (like GetFamilyTreeAsync does for a single component) since the graph as a whole is small - a few hundred rows at most. every person that appears in at least one edge below every kinship edge (GanjoorPersonRelation) and non-family tie (GanjoorPersonAffiliation), merged into one flat list - see GanjoorPersonGraphEdge.Category to tell them apart one node of the people graph GanjoorRelatedPerson.Id GanjoorRelatedPerson.Name true if this person captions a family tree (GanjoorRelatedPerson.FamilyTreeCaption not empty) - used client-side to draw such nodes slightly larger/more prominent, and to link to their /FamilyTree/{id} strict-tree view true unless this node was pulled in only because it's one hop away (a relative or affiliate) from a person actually tagged in a work's verses - see GetCatPersonGraphAsync. Always true for the whole-site graph (GetPersonGraphAsync), where there's no "work" to be tagged within. Client-side, a false value draws the node as secondary (e.g. dashed/dimmer) since it's context the reader wasn't shown directly. one edge of the people graph - either a kinship edge or a non-family tie, flattened to a common shape so the client can draw/list them uniformly first person - directional meaning (if any) depends on Category/TypeValue, same as the underlying GanjoorPersonRelation/GanjoorPersonAffiliation row first person's name, denormalized here so the client doesn't need a second lookup second person second person's name "Relation" for a GanjoorPersonRelation edge, "Affiliation" for a GanjoorPersonAffiliation one - tells the client which enum TypeValue is a numeric value of the numeric value of PersonRelationType (when Category is "Relation") or PersonAffiliationType (when Category is "Affiliation") see GanjoorPersonRelation.DegreeHint - null for an Affiliation edge free-text note a person plus their kinship/affiliation edges, resolved with the other side's name - the read-only counterpart of the pending-suggestion PersonGraphSuggestion (see GanjoorPoemGeoDateTagCorrection.SuggestedPersonGraphJson), served by GET api/people/{id}/relations the subject person kinship edges touching Person (either side) non-family ties touching Person (either side) one GanjoorPersonRelation row, resolved from Person's point of view the underlying GanjoorPersonRelation row's own id - needed by the client to link to /User/SuggestPersonRelationEdit?relationId={id} for suggesting a change/removal of this specific edge the other person in this relation the other person's name (denormalized here so the client doesn't need a second lookup) the kind of kinship edge see GanjoorPersonRelation.DegreeHint free-text note true if the subject person is Person1 in the underlying GanjoorPersonRelation row - needed by the client to render directional types (Parent, Ancestor) correctly one GanjoorPersonAffiliation row, resolved from Person's point of view - same shape/purpose as GanjoorPersonRelationInfo above, but for a non-family tie the underlying GanjoorPersonAffiliation row's own id - used to link to /User/SuggestPersonRelationEdit?affiliationId={Id} for suggesting a change/removal of this edge, same role RelationId plays on GanjoorPersonRelationInfo the other person in this tie the other person's name the kind of non-family tie free-text note true if the subject person is Person1 in the underlying GanjoorPersonAffiliation row a more complete GanjoorPoem View Model Id title cat + parent cats title + title url => slug sample: /hafez/ghazal/sh1 verses text verses text as html (ganjoor.net format) source name source url slug old collection or book name for Saadi's ghazalyiat (طیبات، خواتیم و ....) old collection page url e.g /saadi/tayyebat order when mixed with categories published language poem summary category Next Poem Previous Poem verses Recitations Images Songs Comments poem sections geo/date tags top 6 quoted poems section index poem is claimed by multiple poets couplets count poem correction view model Correction Id poem Id modified verses suggested geo/date tags title original title rhythm original rhythm note date user Id nickname reviewed reviewdate review note review result for title review result for rhythm rhythm 2 original rhythm 2 review result for rhythm 2 rhythm 3 original rhythm 3 review result for rhythm 3 rhythm 4 original rhythm 4 review result for rhythm 4 rhyme letters original rhyme letters rhyme letters review result language original language language review resukt suggested poem summary original poem summary summary review result poem format original poem format poem format review result hide the editors name poem section correction view model Correction Id section id rhythm original rhythm review result for rhythm rhythm 2 original rhythm 2 review result for rhythm2 break from verse1 VOrder review result for break from verse1 VOrder break from verse2 VOrder review result for break from verse2 VOrder break from verse3 VOrder review result for break from verse3 VOrder break from verse4 VOrder review result for break from verse3 VOrder break from verse5 VOrder review result for break from verse5 VOrder break from verse6 VOrder review result for break from verse6 VOrder break from verse7 VOrder review result for break from verse7 VOrder break from verse8 VOrder review result for break from verse8 VOrder break from verse9 VOrder review result for break from verse9 VOrder break from verse10 VOrder review result for break from verse10 VOrder note date user Id nickname reviewed reviewdate review note poem Id poem Id rhyme letters original rhyme letters rhyme letters review result language original language language review resukt poem format original poem format poem format review result hide the editors name poem summary id title url => slug excerpt text whole poem sections poem translation view model id language poem id translated title published comments or description might be empty in anonymous apis contributer name - might be empty in anonymous apis datetime translated verses to string Poet Complete Information poet info poet cat info id Poet Id order Title Description image url suggested by id published selected rejection cause external normal size image url Ganjoor Poet Specification view model Id Poet Id order Contents published suggested by id rejection cause Ganjoor Poet id name description urlslug root cat id short name published on website image url birth year in lunar hijri BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date death year in lunar hijri DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date Home page pin order (zero means not pinned) birth place birth place latitude birth place longitude death place death place latitude death place longitude report comment view model comment id reason code for better grouping: offensive, bogus, other some explanotory text provided by reporter moderation view model record id approved review note quoted poem view model constructor copy constructor record id poem id poem related poem id (this could be a poem we do not have it available) this poem is written and should be displayed prior to the related poem if the related poet poem has multiple poems related to this poem, which one is chosen to be shown at main poems pages? this sort order can be used to pin a record on top related poem peot death date in hijri (used for sorting) related poem poet name related poem poet url related poem poet image full title full url couplet 1 verse 1 is main part couplet 1 verse 2 is main part couplet 1 index related couplet 1 verse 1 is main part related couplet 1 verse 2 is main part related couplet 1 index description published (approved) when a poem contains more than one quoted couplet from another one this should be more than 1 poems (same poem) are claimed by both poets poet id (redundant for simplifying queries) related poet id related indirectly approved review note suggested by Ganjoor Verse View Model global id, auto generated (missing in Ganjoor Desktop database) vorder position text GanjoorSiteBanner PUT ViewModel alternate text target url active Ganjoor Site Banner View Model id image url alternate text target url active Id poet name poet image url poem full title sample: /hafez/ghazal/sh1 couplet index verse1 text verse2 text DateTime private note for bookmark verse translation view mode verse translated text Ganjoor Verse View Model global id, auto generated (missing in Ganjoor Desktop database) vorder couplet index position GanjoorPoemSection index second GanjoorPoemSection index third GanjoorPoemSection index forth GanjoorPoemSection index text language id couplet summary original (Persian) text - only populated for Tajik responses that need to show the Persian original alongside the Tajik transliteration. This is a pure view model (not an EF entity), so no [NotMapped] or migration concerns here. a moderator's decision on a pending GanjoorPersonEditSuggestion review result review note deserialized shape of GanjoorPoemGeoDateTagCorrection.SuggestedPersonGraphJson - a brand new person (optionally linked to other people, new or existing) suggested in one go alongside a poem's geo/date/person tag. See that field's doc comment for the full JSON shape. the node that ends up assigned to the tag's PersonId once approved other people this submission also introduces or links to, referenced from Relations by their LocalKey kinship and/or non-family edges between any of Person/RelatedPeople's local keys one person node in a PersonGraphSuggestion - either a link to an existing, already approved GanjoorRelatedPerson (ExistingPersonId set, the rest ignored) or a brand new person to create (ExistingPersonId null, the rest describing them) key used only to resolve Relations entries within this same submission - never stored beyond approval time set to link to an existing, already approved person instead of creating a new one - when set, every other field on this node is ignored see GanjoorRelatedPerson.FamilyTreeCaption - only meaningful for whichever node is meant to be a tree's named root one edge in a PersonGraphSuggestion, between two nodes referenced by their LocalKey (Person1/ Person2 - matching PersonGraphNode.LocalKey, not a real database id yet). Kind picks which enum RelationType/AffiliationType is parsed against and which live table the edge is materialized into on approval. "family" (default, materialized as GanjoorPersonRelation - RelationType against PersonRelationType) or "affiliation" (materialized as GanjoorPersonAffiliation - AffiliationType against PersonAffiliationType) parsed against PersonRelationType when Kind is "family" parsed against PersonAffiliationType when Kind is "affiliation" only meaningful for a family relation of type Ancestor - see GanjoorPersonRelation.DegreeHint Ganjoor Poem related music track model id poem id a track type artist name artist url album name album url track name track url description broken link golha id approved instead of deleting rejected songs keep track of them rejection cause Suggested by user id (only filled for review) Suggested by user name (only filled for review) to string Related Image Type musueum.ganjoor.net link external link Poem Related Images link id Image Order poem related image type thumbnail image url target page url alternate text is text original source poem id poem url poem full title poem + verse order poem id verse order user contributions user id create date poem corrections section corrections cat edit corrections suggested songs quoted poems comments recitations museum links pinterest links poet spec links poet pictures public user notes history of suggested songs by a user approved rejected grouped by date / user date (day date or month or ...) number of clicks user id user name date (day date or month or ...) number of clicks grouped by user view model number of clicks user id user name summed up view model Number of days Total number Total Number of UserIds Import Job Job Type music catalogue album id singer id singer name url tracks music catalogue singer id name url poet image user image id albums music catalogue track id album id album name url blocked from suggestion reason track is purely music and no singing is done through it http://www.golha.co.uk offline catalogue id name url programs Golha Program id GolhaCollection Id GolhaCollection title program order url mp3 tracks id GolhaProgram Id GolhaProgram track no timing title singer id singer blocked from suggestion reason track is purely music and no singing is done through it Golha Program View Model id title program order url mp3 Golha Track View Model id track no timing title Note Type private public User Note Id User Id User Master Record Id Master Record Item Record Id Item Record DateTime Is Updated by User Last Modified Private / Public content In Reply to Other Note Reference Note Id Status (Public) User Notes Abuse Report Id Note Id Note some explanotory text provided by reporter reporting user Id (nullable to allow deleting users and keeping this part of data if needed) reporting user user note report view model note Id reason text New User Note For Artifact Artifact / Item Id Note Type Contents Reference Note Id (Public) User Notes Abuse Report View Model Id some explanotory text provided by reporter note Safe User Note View Model Id User Id User Name (FirstName + SurName) user image id DateTime Is Updated by User Last Modified Private / Public content Reference Note Id Status Related Entity (Artifact/Item) Name It is set only in cummulative api calls Realated entity Image Id It is set only in cummulative api calls Realated entity Friendly Url (for artifact items it would be ParentFiendlyUrl/ItemFriendlyUrl ) It is set only in cummulative api calls Related entity external normal size image url Related Item Artifact Name It is set only in cummulative api calls child notes It os set only in certain cases prepare note datetime Author Id Name Name in original language bio Cover Image Cover Image Id external image url Last Modified Author Role Id Author Role a book might have different PDFBook or MultiVolumePDFCollection instances Id book name description Cover Image Cover Image Id external cover image url Last Modified Authors Attributes pdf books Book Script Type Printed Manuscript id cat id cat full title cat full url start from this poem book id book title page number threshold queue time started start time last update updated by user current poem id progress current page number finished finish time Multi Volume Book Id book id book Name Description volume count pdf books OCR Queued Item id PDF Book Id PDF Book Id book id book Publish Status Title secondary or complementary title authors (descriptive) ISBN description language is translation translators (descriptive) title in original language publisher (descriptive) publishing date (descriptive) publishing location (descriptive) publishing number (تیراژ) claimed page count MultiVolumePDFCollection Id MultiVolumePDFCollection Volume Order Date/Time Last Modified for caching purposes PDF File external url for PDF File Cover Image Cover Image Id external cover image url original source name original source url original file url contributers Pages page count Attributes file check sum original file name storage folder name Book Script Type PDF Source Id PDF Source tags view models Binary Tagged Items Titles of Items in Contents ocred ocr date time book text PDF Page Id PDF Book Id page number Thumbnail Image Thumbnail Image Id external thumbnail image url description Last Modified Attributes full resolution image width full resolution image height ocred ocr date time page text pdf book tags view models PDF Source (Web Sites) id Name Url Description a fake PDFBook for scheduling downloads Id Download Order Title authors (descriptive) description language is translation translators (descriptive) original source name original source url original file url processed process result result id Author Role Count Role Counts New PDFBook view model PDF File Path skip uploading to external FTP Site book id Title secondary or complementary title authors (descriptive) ISBN description language is translation translators (descriptive) title in original language publisher (descriptive) publishing date (descriptive) publishing location (descriptive) publishing number (تیراژ) claimed page count MultiVolumePDFCollection Id Volume Order original source name original source url original file url writer id writer 2 id writer 3 id writer 4 id translator id translator 2 id translator 3 id translator 4 id collector id (مصحح) collector 2 id (مصحح) Other contributing role other contributing role Other contributing role 2 other contributing role 2 PDF Source Id Book Script Type PDFBook Ganjoor Link Suggestion Ganjoor Post Id ganjoor url ganjoor title pdf book id page number is this the text original source? PDF Page OCR Data View Model Id full resolution image width full resolution image height ocred page text Upload Session Id Session Type User User Id Upload Start Time Upload End Time Process Start Time Process End Time Process Progress Status Uploaded Files Upload Session File Id Upload Session Id ContentDisposition ContentType FileName Length Name File Path File check sum for mp3 files Process Result Process Result Message Upload Session Process Status Not Started Started Finished Upload Session Types Generic New Audio New or replace audio Uploaded Item View Model filename process result process result message Upload End Time Upload User Name Process Start Time progress progress process end time Artifact Item Service get all artifacts (including CoverImage info but not items or attributes info) get tagged publish artifacts (including CoverImage info but not items or tagibutes info) gets specified artifact info (including CoverImage + images + attributes) get specific artifact info with limited number of images get artifact images according to start and count params gets specified artifact info (including CoverImage + images + tagibutes) edit master record Set Artifact Cover Item Index get tag bundle by frindly url get max lastmodified artifact date for caching purposes get all tags get tag bu friendly url add tag edit tag changes order of tags based on their position in artifacts true => up, false => down the other tag which its Order has been changed changes order of tags based on their position in artifact items true => up, false => down the other tag which its Order has been changed add artifact tag value changes order of tag values based on their position in an artifact true => up, false => down the other tag value which its Order has been changed changes order of tag values based on their position in an item true => up, false => down the other tag value which its Order has been changed get tag value bundle by frindly url get tag value by frindly url edit artifact tagibute value apply on all same value tags remove artfiact tag value add item tag value edit item tag value apply on all same value tags remove item tag value gets specified artifact item info (including images + attributes) update artifact item add new artifact import from external resources loc/princeton/harvard/qajarwomen/hathitrust/penn/cam/bl/folder/walters/cbl 119 golestan-baysonghori reschedule jobs due to a bug in loc json outputs some artifacts with more than 1000 pages were downloaded incompletely import jobs Bookmark Artifact get artifact user bookmarks Bookmark Item get item user bookmarks update bookmark note delete user bookmark /// get user bookmarks (artifacts and items) Add Note to Artifact Add Note to Artifact Item Edit Note sending null here means user is a moderator delete note sending null here means user is a moderator or the note is being deleted in a recursive delete of referenced notes list of notes deleted get artifact private user notes get artifact public user notes get item artifact private user notes get item artifact item public user notes Get All USer Notes Get All Public Notes suggest ganjoor link get Unsynchronized image count finds what the method name suggests get suggested ganjoor links Review Suggested Link remove ganjoor link Temporary api Synchronize suggested link get suggested pinterest links suggest pinterest link Review Suggested Pinterest Link remove pinterest link Synchronize suggested pinterest link an incomplete prototype for removing artifacts start filling GanjoorLink table OriginalSource values report a public note id of report record Get a list of reported notes delete a report for abuse in public user notes delete a reported user note (accept the complaint) start removing original images Search Artifacts search artifact items start setting an artifact items as text original source upload artifact to external server create images for ganjoor create images for ganjoor - offline add item to an artifact ai contributions stats service user contributions summed up stats of users (Days and UserIds are invalid) users grouped by signup date approved edits daily approved edits grouped by user summed up stats of approved poem corrections approved section edits daily approved section edits grouped by user summed up stats of approved section corrections approved cat edits daily approved cat edits grouped by user summed up stats of approved cat corrections approved related songs daily approved related songs grouped by user summed up stats of approved related songs approved quoted poems daily approved quoted poems grouped by user summed up stats of approved quoted poems approved comments daily approved comments grouped by user summed up stats of approved comments approved recitations daily approved recitations grouped by user summed up stats of approved recitations approved museum links daily approved museum links grouped by user summed up stats of approved museum links approved pinterest links daily approved pinterest links grouped by user summed up stats of approved pinterest links approved poet spec lines daily approved poet spec lines grouped by user summed up stats of approved poet spec lines approved poet photos daily approved poet photos grouped by user summed up stats of approved poet photos approved user notes approved user notes grouped by user summed up stats of approved user notes donation service new donation delete donation update donation new expense update expense delete expense parse html of https://ganjoor.net/donate/ and fill the records regenerate donations page returns all donations get donation by id returns all expenses get expense by id Show Donating Information (temporary switch off/on) faq service interface get categories get category by id add a new faq category update an existing faq category delete a faq category get pinned items get category items get item by id add a new faq item update an existing faq item delete a faq item numbering service add numbering update an existing numbering (only name) delete numbering get numbering by id get all numberings get numberings for a cat get numberings for direct subcats of parent cat get all numbering patterns for a couplet start counting generate missing default numberings and start counting related people (family tree / person tagging) service. A new person is normally created via the geo/date/person tag correction's SuggestedPersonGraphJson, materialized on moderator approval (see GanjoorService-ModeratePoemCorrection.cs). Editing an already-approved person's own fields (e.g. adding a FamilyTreeCaption after the fact) goes through the suggest/review queue below (SuggestPersonEditAsync / ModeratePersonEditSuggestionAsync) - there is no direct-edit path; nothing ever writes to a GanjoorRelatedPerson's fields except that approval step and the original creation-on-approval in GanjoorService-ModeratePoemCorrection.cs. get all people (for the search-as-you-type person picker) get person by id get people who caption a family tree (GanjoorRelatedPerson.FamilyTreeCaption not empty) - used as the entry points for browsing family trees get a person along with all their kinship/affiliation edges (resolved with the other side's name), for the read-only person/family-tree browsing page get the (approved, materialized) poem geo/date tags that name this person, each carrying enough of its Poem to link to it get the whole connected kinship component reachable from this person (ancestors, descendants, spouses, siblings - whichever edges connect to it, transitively), for the interactive family-tree chart at /FamilyTree/{id} submit a suggested edit to an already-approved person's own fields - goes into the pending queue, does not change the person itself until a moderator approves it get the next unreviewed person-edit suggestion (for the moderator queue), including the target person's current fields (for a before/after diff) and the suggester's nickname unreviewed person-edit suggestion count apply a moderator's decision to a pending person-edit suggestion. On Approved, copies the suggestion's Suggested* fields onto the target GanjoorRelatedPerson (Id and MachineGenerated on the person are left untouched); any other result just marks the suggestion reviewed/rejected without touching the person. get a single kinship edge by its own id, with both sides' names resolved - used by /User/SuggestPersonRelationEdit?relationId={relationId} to show what it's about get a single affiliation edge by its own id, with both sides' names resolved - used by /User/SuggestPersonRelationEdit?affiliationId={affiliationId} to show what it's about submit a suggested addition, change or removal of a kinship edge - goes into the pending queue, does not change anything until a moderator approves it get the next unreviewed relation-edit suggestion (for the moderator queue) unreviewed relation-edit suggestion count apply a moderator's decision to a pending relation-edit suggestion. On Approved: Add creates a new GanjoorPersonRelation, Modify updates the existing one ExistingRelationId points to, Remove deletes it (and auto-rejects any other still-pending suggestion that also targeted that same now-gone relation, so it doesn't dangle). get the whole known network of people (every person with at least one kinship edge or non-family tie, plus every one of those edges/ties) for the force-directed "ontology" explorer opened via the PeopleExplorer.open() modal (formerly the standalone page /PeopleGraph) - see GanjoorPersonGraphViewModel get the network of people relevant to one work/category (a poet's whole corpus, one book like the Shahnameh, or a narrower story within it) - every person tagged in a poem under catId's subtree, plus their relatives/affiliates one hop out even if never tagged themselves - see GanjoorPersonGraphNode.DirectlyTagged Ganjoor Poems Content Privider Service Get List of poets Get list of books (GanjoorCat entries whose CatType is Book), sorted alphabetically by name. Not to be confused with GetBooksAsync() below, which lists GanjoorCat entries by their (separate, legacy) BookName field for cover-image generation. optional, only books whose name contains this (case-insensitive) optional, only books belonging to this poet get poet by id get poet by url poet image id by url get cat by id get cat by url Update category extra info get list of books generate missing book covers get page by url get page url by id get redirect url for a url delete page delete a poem Get Poem By Id get poem verses Get Poem By Url get poem recitations (PlainText/HtmlText are intentionally empty) get poem sections get user up votes for the recitations of a poem get poem images by id (some fields are intentionally field with blank or null), EntityImageId : the most important data field, image url is {WebServiceUrl.Url}/api/images/thumb/{EntityImageId}.jpg or {WebServiceUrl.Url}/api/images/norm/{EntityImageId}.jpg get poem related songs get poem comments TopRated, Oldest, or Newest get a single comment information (replies are not included) like / dislike / clear rating for a comment +1: like, -1: dislike, 0: remove previous rating get the requesting user's own rating values for all comments of a poem (mirrors GetUserPoemRecitationsUpVotes - meant to be merged client-side into an already fetched, anonymously cacheable GetPoemComments/GetPoemById result) get a section related sections poem id section index if sent 0 or less returns all items send poem corrections last unreviewed user correction for a poem user suggested songs get user or all corrections if sent empty returns all corrections effective corrections for poem get correction by id delete unreviewed user corrections for a poem get next unreviewed correction unreview correction count moderate poem correction break a poem from a verse forward update related sections next unreviewed track get track of user song suggestions suggest song get unreviewed count review song direct insert song get song by id modify a published song delete poem song by id new comment update user's own comment link or unlink user's own comment to a coupletIndex if null then unlinks couplet summary delete a reported comment delete user own comment delete anybody's comment publish awaiting comment get recent comments Guid.Empty report a comment id of report record delete a report Get list of reported comments Get Similar Poems accroding to prosody and rhyme informations language tagged poem sections modify page modify poem => only these fields: NoIndex, RedirectFromFullUrl, MixedModeOrder modify poet create new poet delete poet chaneg poet image returns metre list (ordered by Rhythm) get a random poem (2 = from hafez) import from sqlite import a catgory from sqlite Apply corrections from sqlite export to sqlite start generating gdb files start exporting all published data (poets/categories/poems/verses) to the public git-tracked JSON data set, committing and pushing changes since the last run start exporting the Tajik (Cyrillic) overlay data to its own separate git-tracked JSON data set — only entities that actually have a Tajik translation are included (re)build local Ganjoor content (poets/categories/poems/verses/sections + their pages) from a public data export tree, read locally or over HTTP. Safe to re-run — existing entities (by id) are left untouched, only missing ones are added. true: fetch over HTTP (location is a base URL). false: read from a local folder (location is a path). base URL or local folder path of the exported data tree 0 imports every poet; a specific id imports only that poet (useful on a slow connection) examine site pages for broken links examine comments for long links start filling poems couplet indices refill couplet indices fill section couplets count regenerate poem full titles to fix an old bug start finding rhymes for single couplets separate verses in poem.PlainText with Environment.NewLine instead of SPACE clean cache for paeg by id clean cache for page by url clean cache for page by comment page modifications history get old version Search batch rename re sulg cat poems find poem rhyme find poem section rhyme find category poem rhymes Start finding missing rhthms find poem rhythm find category poem rhymes generate category TOC directly insert generated TOC start generating sub cats TOC build sitemap build tj.ganjoor.net site map start updating stats page start updating mundex page Switch Bookmark for couplet Bookmark couplet if it is not delete user bookmark /// to make sure a user can not delete another user's bookmarks modify bookmark private note to make sure a user can not modify another user's bookmarks get user ganjoor bookmarks (only Id, CoupletIndex and DateTime are valid) get verse bookmarks get user bookmarks (artifacts and items) regenerate half centuries get centuries with published poets start generating related sections info get next ganjoor section probable metre get a list of ganjoor poem sections probable metres save ganjoor poem probable metre problable metre id return list of suggested spec lines returns specfic suggested line for poet next unpublished suggested line for poets npublished suggested lines count for poets add a suggestion for poets spec lines modify a suggestion for poets spec lines reject a suggestion for poets spec lines delete published suggested spec line delete a category set category poems language tag set category poem format tag for poems consisting of a single whole poem section regenerate TOCs Finding Category Poems Duplicates list of category saved duplicated poems manually add a duplicate for a poems delete duplicate start removing category duplicates sectionizing poems start band couplets fix get couplet sections get all poem sections regenerate poem sections (dangerous: wipes out existing data) get a specific poem section delete a poem section last unreviewed user correction for a section get user section corrections if sent empty returns all corrections send a correction for a section moderate poem section correction delete unreviewed user corrections for a poem section get next unreviewed correction for a poem section unreviewed poem section correction count get section correction by id effective corrections for section regenerate category related sections transfer poems and sections from a meter to another one add poem geo tag update poem tag delete poem tag get poem tags ordered by LunarDateTotalNumber then by Id get a categoty poem tags synchronize naskban links number of synched items justify naskban links import naskban ganjoor matchings import paper sources from museum discover poet naskban paper sources category paper sources mark naskban links as human reviewed mark naskban links a text original for a category delete poem related naskban images by url get category poem related images filling poem formats extracting quoted poems regenerate related poems pages generate related poems page discover related poems get quoted poems for a poem two poems quoted records get quoted poems get quoted by id insert quoted poem update quoted poem delete quoted by id suggest new quote get user or all quoted suggestions if sent empty returns all suggestions next unmoderated quoted poem unmoderated quoted poems count moderate quoted poems digital source from tag tag with sources update digital sources stats add page build word counts fill CategoryWordCounts.RowNmbrInCat fill CategoryWordCountSummaries category words count category words summary comparison of word counts for poets get CategoryWordCount for a specific term in a category tajik import from sqlite tajik poets tajik page by url search tajik poems re-runs the Tajik poet/category HTML generators for every already-imported poet and category page, overwriting their stored TajikHtmlText - needed one-time after changing either generator function, since the normal SQLite import skips pages that already exist. Runs as a background job; returns immediately once the job is queued. recovery job: regenerates TajikHtmlText for Tajik poems whose stored HTML currently contains a link (the signature left by the CatPage id corruption bug - correct poem HTML never contains one), overwriting just those with correct verse markup. fill couplet summaries using open ai fill poem summaries using open ai geo tag poems using AI send cat correction delete unreviewed user corrections for a cat last unreviewed user correction for a cat get user or all corrections for categories if sent empty returns all corrections cat effectinve corrections get cat correction by id get next unreviewed cat correction unreviewed cat corrections count moderate cat correction translation service implementation add language update an existing language delete language get langauge by id get all languages geo location service add new location update an existing location delete location get location by id get all locations IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation IArtifactService implementation start setting an artifact items as text original source start filling GanjoorLink table OriginalSource values import from http://www.qajarwomen.org 43117279 atame http://www.qajarwomen.org/fa/items/1018A10.html from http://www.bl.uk grenville_xli_f001r from http://cudl.lib.cam.ac.uk MS-RAS-00258 import from https://viewer.cbl.ie 119 golestan-baysonghori from https://curiosity.lib.harvard.edu example: https://curiosity.lib.harvard.edu/islamic-heritage-project/catalog/40-990114893240203941 from https://catalog.hathitrust.org 006814127 from IIIF manifest example: https://viewer.cbl.ie/viewer/api/v1/records/Per_268/manifest/ or https://iiif.bodleian.ox.ac.uk/iiif/manifest/bcbfd832-086b-4874-80f8-87500e0de704.json from https://www.loc.gov m084 boostan1207 plmp due to a bug in loc json outputs some artifacts with more than 1000 pages were downloaded incompletely from http://www.library.upenn.edu/ MEDREN_9949222153503681 import from server folder C:\Tools\batches\florence shahname-florence https://t.me/dr_khatibi_abolfazl/888 start appending from server folder from http://www.thedigitalwalters.org/01_ACCESS_WALTERS_MANUSCRIPTS.html W619 golestan-walters-01 start removing original images upload artifact to external server from http://pudl.princeton.edu/ dj52w476m get all artifacts (including CoverImage info but not items or tagibutes info) get tagged publish artifacts (including CoverImage info but not items or tagibutes info) gets specified artifact info (including CoverImage + images + tagibutes) get specific artifact info with limited number of images get artifact images according to start and count params gets specified artifact info (including CoverImage + images + tagibutes) edit master record Set Artifact Cover Item Index get tag bundle by frindly url get max lastmodified artifact date for caching purposes get all tags get tag bu friendly url get tag value by frindly url add tag edit tag changes LastModified field value for tagged artifacts and items changes order of tags based on their position in artifacts true => up, false => down the other tag which its Order has been changed changes order of tags based on their position in artifact items true => up, false => down the other tag which its Order has been changed get tag value bundle by frindly url add artifact tag value remove artfiact tag value edit artifact tag value apply on all same value tags add item tag value remove item tag value edit item tagibute value apply on all same value tags changes order of tag values based on their position in an artifact true => up, false => down the other tag value which its Order has been changed changes order of tag values based on their position in an item true => up, false => down the other tag value which its Order has been changed gets specified artifact item info (including images + tagibutes) update artifact item add new artifact import from external resources pdf/loc/princeton/harvard/qajarwomen/hathitrust/penn/cam/bl/folder/walters/cbl/append/iiif 119/foldername golestan-baysonghori/artifact id upload artifact to external server upload artifact to external server reschedule jobs an incomplete prototype for removing artifacts import jobs Bookmark Artifact get artifact user bookmarks Bookmark Item get item user bookmarks update bookmark note delete user bookmark /// get user bookmarks (artifacts and items) Add Note to Artifact Add Note to Artifact Item Edit Note delete note sending null here means user is a moderator or the note is being deleted in a recursive delete of referenced notes list of notes deleted get artifact private user notes recursvie call to build notes tree get artifact public user notes get item artifact private user notes recursvie call to build notes tree for iten get item artifact item public user notes Get All USer Notes Get All Public Notes report a public note id of report record delete a report for abuse in public user notes delete a reported user note (accept the complaint) Get a list of reported notes suggest ganjoor link get Unsynchronized image count finds what the method name suggests get suggested ganjoor links Review Suggested Link remove ganjoor link Temporary api Synchronize suggested link get suggested pinterest links suggest pinterest link Review Suggested Pinterest Link remove pinterest link Synchronize suggested pinterest link Search Artifacts search artifact items add item to an artifact Database Contetxt Picture File Service Configuration Background Task Queue Instance User Service Messaging service Ganjoor Service constructor British Library Tile Downloader And Mixer Constructor find image encodder contributions stats service implementation user contributions users grouped by signup date summed up stats of users (Days and UserIds are invalid) approved edits daily approved edits grouped by user summed up stats of approved poem corrections approved section edits daily approved section edits grouped by user summed up stats of approved section corrections approved cat edits daily approved cat edits grouped by user summed up stats of approved cat corrections approved related songs daily approved related songs grouped by user summed up stats of approved related songs approved quoted poems daily approved quoted poems grouped by user summed up stats of approved quoted poems approved comments daily approved comments grouped by user summed up stats of approved comments approved recitations daily approved recitations grouped by user summed up stats of approved recitations approved museum links daily approved museum links grouped by user summed up stats of approved museum links approved pinterest links daily approved pinterest links grouped by user summed up stats of approved pinterest links approved poet spec lines daily approved poet spec lines grouped by user summed up stats of approved poet spec lines approved poet photos daily approved poet photos grouped by user summed up stats of approved poet photos approved user notes approved user notes grouped by user summed up stats of approved user notes Database Context Configuration IAppUserService instance constructor تبدیلگر CorrectionReviewResult تبدیل به رشته donation service new donation update donation delete donation new expense update expense delete expense returns all donations get donation by id returns all expenses get expense by id donation page row date amount donor usage remaining parse html of https://ganjoor.net/donate/ and fill the records regenerate donations page Show Donating Information (temporary switch off/on) Database Context Background Task Queue Instance Ganjoor Service configuration constructor faq service implementation get categories get category by id add a new faq category update an existing faq category delete a faq category get pinned items get category items get item by id add a new faq item update an existing faq item delete a faq item Database Context constructor Ganjoor User Service IMemoryCache Ganjoor Service Sign Up Email Subject subject Sign Up Email Html Content html content Start signup process using email finalize signup and assign his or him comments to him or her modify existing user /*update related entities cache*/ remove user data numbering service implementation add numbering update an existing numbering (only name) delete numbering get numbering by id get all numberings get numberings for a cat get numberings for direct subcats of parent cat get all numbering patterns for a couplet start counting generate missing default numberings and start counting Database Context Background Task Queue Instance constructor poem format convertor تبدیل به رشته related people service implementation get all people get person by id get people who caption a family tree get a person along with all their kinship/affiliation edges get the (approved, materialized) poem geo/date tags that name this person get the whole connected kinship component reachable from this person submit a suggested edit to an already-approved person's own fields get the next unreviewed person-edit suggestion for the moderator queue unreviewed person-edit suggestion count apply a moderator's decision to a pending person-edit suggestion removes a person and every row that would otherwise block that deletion at the database level (kinship edges, affiliations, and other pending relation-edit suggestions touching them - all Restrict-on-delete FKs, see RMuseumDbContext.OnModelCreating), plus detaches (but does not delete) any approved poem geo/date tag that named them, since the tag itself may still carry a real location/date worth keeping. get a single kinship edge by its own id, with both sides' names resolved get a single affiliation edge by its own id, with both sides' names resolved - the Kind == Affiliation counterpart of GetRelationByIdAsync, used the same way by /User/SuggestPersonRelationEdit?affiliationId={id} submit a suggested addition, change or removal of a kinship edge get the next unreviewed relation-edit suggestion for the moderator queue unreviewed relation-edit suggestion count apply a moderator's decision to a pending relation-edit suggestion get the whole known network of people, for the force-directed "ontology" explorer get every category id in the subtree rooted at catId (catId itself plus every descendant, walked breadth-first) - a lighter-weight, purpose-built counterpart of GanjoorService._populateCategoryChildren (which builds full category objects via _GetCatById); this only needs ids, so it queries them directly get the network of people relevant to one work/category (e.g. a poet's Shahnameh, or one story within it like Nezami's Leyli o Majnoon) - the category-scoped counterpart of GetPersonGraphAsync, for the "شخصیت‌ها" tab on a category/poet page. Starts from every person directly tagged (PoemGeoDateTag.PersonId) in a poem under catId's subtree, then adds their relatives/affiliates one hop out even when those relatives are never tagged in the work themselves - so, say, a hero's father still shows up if he's known but never named in a verse, which helps a reader unfamiliar with the story rather than leaving the tree looking broken. Those one-hop additions are marked DirectlyTagged = false so the client can draw them as secondary. shared node-list builder for GetPersonGraphAsync/GetCatPersonGraphAsync - directlyTaggedIds null means "everyone counts as directly part of the graph" (the whole-site graph has no notion of one-hop additions) shared edge-list builder for GetPersonGraphAsync/GetCatPersonGraphAsync - flattens GanjoorPersonRelation and GanjoorPersonAffiliation rows into the common GanjoorPersonGraphEdge shape, resolving both sides' names from personById Database Context used to find every user holding the Ganjoor:Modify permission, to notify them when a new suggestion needs review - same permission the moderation endpoints themselves require used to notify moderators of a new pending suggestion, and submitters of its outcome - same service/pattern GanjoorService uses for poem corrections and song suggestions constructor notify every user holding the Ganjoor:Modify permission that a new suggestion of the given kind is pending review at reviewPageUrl - shared by both suggestion types below IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation IGanjoorService implementation Switch Bookmark for couplet Bookmark couplet if it is not Bookmark Verse bookmark comment get user ganjoor bookmarks (only Id, CoupletIndex and DateTime are valid) get verse bookmarks delete user bookmark /// to make sure a user can not delete another user's bookmarks modify bookmark private note to make sure a user can not modify another user's bookmarks get user bookmarks (artifacts and items) Get list of books (GanjoorCat entries whose CatType is Book), sorted alphabetically by name. Not to be confused with GetBooksAsync() elsewhere in this class, which lists GanjoorCat entries by their (separate, legacy) BookName field for cover-image generation. optional, only books whose name contains this (case-insensitive) optional, only books belonging to this poet category word counts category words summary comparison of word counts for poets ot subcats CategoryWordCount for a specific term in a category build word counts fill CategoryWordCounts.RowNmbrInCat fill CategoryWordCountSummaries set category poem format tag for poems consisting of a single whole poem section set category poems language tag re sulg cat poems batch rename list of category saved duplicated poems Update category extra info find category poem rhymes get centuries with published poets regenerate half centuries same as , but against an explicitly-passed context instead of the injected — needed so background jobs that construct their own short-lived (because the request-scoped may already be disposed by the time a background job runs) can call this too, e.g. right after a public-data import finishes. like / dislike / clear rating for a comment +1: like, -1: dislike, 0: remove previous rating get the requesting user's own rating values for all comments of a poem (meant to be merged client-side into an already fetched, anonymously cacheable comment list) get digital source from tag tag with sources update digital sources stats Finding Category Poems Duplicates start removing category duplicates find category poem rhymes directly insert generated TOC generate category TOC extracts the searchable part of a poem title, discarding pure boilerplate like "غزل شمارهٔ ۱" or "بخش ۴۷" which carries no information beyond the ordinal number, while keeping either the whole title (if it has no number at all, e.g. "طهمورث") or just the descriptive part after the number (e.g. "بخش ۴۷ - تشبیه کردن قرآن..." keeps "تشبیه کردن قرآن...") - returns "" when there is nothing worth indexing make plain text poem/section title - its searchable part (if any) is included so titles like "بخش ۴۷ - تشبیه کردن ..." are findable through search, while pure boilerplate like "غزل شمارهٔ ۱" is silently skipped separate verses in poem.PlainText with Environment.NewLine instead of SPACE examine site pages for broken links extracts normalized plain text from (possibly malformed) HTML the same way a spec-compliant parser (and so also the sanitizer's own parser) sees it, so it can be compared before/after sanitizing to detect whether sanitizing dropped real text true if sanitizing the comment dropped a meaningful chunk of the user's actual text - not just markup/attributes, and not a harmless space lost when an inline tag gets unwrapped. This happens when invalid/unclosed markup causes real comment text to end up nested inside a tag the sanitizer correctly removes entirely (e.g. a stray/unclosed tag that swallows the rest of the comment as its "content", or an actually-disallowed tag such as script/style whose whole subtree is removed) shown to the user (via _BuildSanitizerDroppedTextError) when sanitizing had to drop real text. Deliberately says nothing about "tags", "<" or ">" - ordinary users don't know what those are and have usually never typed one themselves; almost always this happens because they pasted the text in from somewhere else (Word, a chat app, a web page) that silently carried invalid/broken formatting along with it. builds the error string returned to the client when sanitizing dropped real text. This is a JSON object encoded as a string (not a status-code/contract change) so it still flows through every existing "the API error is just a display string" code path unchanged, while GanjooRazor can additionally recognize and unpack it to show the user exactly what got dropped (see SanitizerTextDroppedInfo on the GanjooRazor side) sanitizes comment HTML; TextWasDropped is true when the sanitizing process removed a meaningful chunk of the user's actual text (see _CommentSanitizationDroppedText) - callers should not silently save Html in that case, but ask the user to fix their markup instead (RemainingPlainText/_BuildSanitizerDroppedTextError let them show what was dropped) examine comments for long links start filling poems couplet indices refill couplet indices fill section couplets count regenerate poem full titles to fix an old bug start finding rhymes for single couplets transfer poems and sections from a meter to another one moderate cat correction moderate poem correction materializes a GanjoorPoemGeoDateTagCorrection.SuggestedPersonGraphJson payload: creates any brand new person nodes it describes (or reuses an existing one when a node names ExistingPersonId), then creates the kinship (GanjoorPersonRelation) and/or non-family (GanjoorPersonAffiliation) edges between them. Returns the resolved id of the "person" node (the one that ends up assigned to the tag's PersonId), or an error message. SuggestedPersonGraphJson value - see its doc comment for the expected shape moderate poem section correction get user section corrections if sent empty returns all corrections effective corrections for section last unreviewed user correction for a section send a correction for a section delete unreviewed user corrections for a poem section get next unreviewed correction for a poem section get section correction by id unreview poem section correction count start updating stats page synchronize naskban links synchronize naskban links number of synched items mark naskban links as human reviewed mark naskban links a text original for a category delete poem related naskban images by url justify naskban links import naskban ganjoor matchings import naskban ganjoor matchings number of synched items fill couplet summaries using open ai fill poem summaries using open ai geo tag poems using AI category paper sources discover poet naskban paper sources import paper sources from museum return list of suggested spec lines returns specific suggested line for poets next unpublished suggested line for poets upublished suggested lines count for poets add a suggestion for poets spec lines modify a suggestion for poets spec lines delete a suggestion for poets spec lines delete published suggested spec line great-circle distance between two points, in kilometers - used only for catching obviously-duplicate new-location suggestions (someone retyping a place that's already in the catalog under slightly different coordinates), so this doesn't need to be geodesy-grade precise, just good enough at the few-kilometers scale add poem geo tag update poem tag delete poem tag get poem tags ordered by LunarDateTotalNumber then by Id get a categoty poem tags filling poem formats sectionizing poems filter secion verses get couplet sections get all poem sections regenerate poem sections (dangerous: wipes out existing data) get a specific poem sections delete a poem section start band couplets fix Start finding missing rhthms get next ganjoor poem probable metre get a list of ganjoor poems probable metres save ganjoor poem probable metre problable metre id how many ids are grouped into each id-index shard file — kept small enough that a shard stays a cheap single fetch, large enough that the id-space doesn't produce an unreasonable number of tiny files. 2000 ids/shard means ~500 shard files for Ganjoor's current poem count. Guards against two runs of the same export job (keyed by job name — "PublicDataExport" / "TajikPublicDataExport") ever executing concurrently against the same git working copy. This exists because deleting a job's row on the admin Jobs page only removes its database record — it does not stop the background Task (or its child git.exe process) that's still actually running. Without this guard, triggering the job again while a previous run hadn't finished starts a second concurrent git process against the same folder, which is exactly what produces a ".git/index.lock: File exists" failure. Returns true (and marks as running) if no run of this job was already in progress; false if one was, in which case the caller should refuse to start a second one rather than queueing a background task that would race the first. start exporting all Ganjoor data (poets/categories/poems/verses) belonging to published poets to a git-tracked JSON tree and pushing it to the configured remote. User-linked tables (comments, bookmarks, visits, corrections, accounting, ...) are never touched by this code path — see RMuseum.Models.Ganjoor.PublicExport for the allowlisted shape of what actually gets written. Batch-loads this poet's entire sections/verses in two queries (instead of the two-per-poem queries the old per-poem approach ran), then walks the poet's category tree writing everything from memory. This is the fix for the export consistently taking about as long on every run regardless of how much content changed — the "skip unchanged files" logic in DeterministicJsonWriter only ever saved disk writes, never the DB round-trips, which were the actual dominant cost (roughly 3 sequential queries per poem — tens of thousands of round-trips for the full corpus, every run). recursively writes _cat.json for and every poem directly under it, then recurses into child categories. Returns the number of poems written in this subtree (for manifest counts). Only the poet-level Published flag is a real visibility gate in this codebase (see GetPoets) — GanjoorCat.Published and GanjoorPoem.Published are not checked anywhere the live site actually serves content (GetCatByUrl/GetPoemByUrl ignore them entirely), so this export doesn't filter on them either; every category/poem under a published poet is exported. Pure in-memory write — no DB access. Sections/verses/metre are pre-loaded by the caller (see ) instead of queried per poem. generates the repo-root API.md every run, so the docs can never drift out of sync with UrlTemplates/IdIndexShardSize in manifest.json. Not hand-edited — if you want to add prose, add it here, not in the generated file. generates the repo-root README.md every run — the landing document GitHub shows by default, so this is where a first-time visitor's "where do I even start?" question needs to be answered in the first few lines, before they'd ever think to look for API.md. Category (and root-poet) pages don't carry a real production page id in the public export — GanjoorPage isn't part of that data set — so this importer mints one deterministically from the category id, kept well clear of any real id range so it can never collide with an actual production GanjoorPage/GanjoorPoem id. Poem pages don't need this: they reuse the poem's own id, matching the convention already used by _ImportSQLiteCatChildren (see GanjoorService-SQLiteImport.cs). (Re)builds Ganjoor content — poets, categories, poems, verses, sections, and their GanjoorPage routing entries — from a public data export tree, read either from a local `git clone` or fetched over HTTP. Safe to run against an empty database (bootstrap) or one that already has some content (merge): every entity is looked up by its id first and only inserted if missing, so re-running never duplicates or overwrites anything — including content a developer may have hand-edited locally after a previous import. true: fetch over HTTP (location is a base URL). false: read from a local folder (location is a path). base URL or local folder path of the exported data tree 0 imports every poet in the export's manifest; a specific id imports only that poet — useful on a slow connection, or when a developer only needs one poet's data for local testing moderate quoted poems suggest new quote get user or all quoted suggestions if sent empty returns all suggestions next unmoderated quoted poem unmoderated quoted poems count discover related poems regenerate related poems pages generate related poems page parse related pages get quoted by id insert quoted poem update quoted poem delete quoted by id get quoted poems get quoted poems for a poem two poems quoted records extracting quoted poems parse related pages get a section related sections poem id section index if sent 0 or less returns all items regenerate category related sections update related sections info (after metreId or rhyme for one of these sections changes) start generating related sections info build sitemap build tj.ganjoor.net site map get poem related songs user suggested songs checks a user supplied track and rewrites its url to canonical form, returns null when it is acceptable suggest song get unreviewed count next unreviewed track get track of user song suggestions review song direct insert song get song by id modify a published song delete a poem song by id start generating gdb files export to sqlite Apply corrections from sqlite import from sqlite import a catgory from sqlite make html text start updating stats page tajik poets tajik page by url short plain-text excerpt (first non-empty line) for a Tajik poem's next/previous nav link Tajik counterpart of PrepareHtmlText (GanjoorService-SQLiteImport.cs) - same VersePosition-based structure/CSS classes, but each verse's Persian original (GanjoorVerse.OriginalText, set just before this is called) is rendered as an extra <p class="tg-fa" dir="rtl"> line directly under its Tajik line, for an interlinear/bilingual display. Computed fresh per-request rather than stored, since it's cheap given the verses are already loaded for the response. re-runs the Tajik poet/category HTML generators for every already-imported poet and category page, overwriting their stored TajikHtmlText. Needed one-time after changing either generator function (e.g. adding poem excerpts to the table of contents), since the normal SQLite import skips pages that already exist and never refreshes them. Runs as a background job (same pattern as TajikImportFromSqlite) since the number of poets/categories/poems involved can be large enough to exceed a normal request timeout; progress can be tracked the same way as other long running jobs (e.g. the Admin area's LongRunningJobs page). recovery job: regenerates TajikHtmlText for every Tajik poem from its TajikVerses, overwriting whatever is currently stored. GanjoorTajikVerse rows carry no VersePosition of their own, so it's recovered by joining back to the original GanjoorVerses row with the same Id (the SQLite import sets GanjoorTajikVerse.Id = the original verse's Id). Only processes poems whose stored TajikHtmlText currently contains a link - correct poem HTML (from PrepareHtmlText) is pure verse markup (.b/.m1/.m2/.n/.l) and never contains an <a href=...>, so a link inside it is the signature the CatPage id bug left behind (it overwrote the affected poems with category-style link-list HTML). Scoping to that signature instead of every poem cuts the run down to just the actually-corrupted ones. Search You need to run this scripts manually on the database before using this method: CREATE FULLTEXT CATALOG [TajikPoemPlainTextCatalog] WITH ACCENT_SENSITIVITY = OFF AS DEFAULT CREATE FULLTEXT INDEX ON [dbo].[TajikPoems]( [TajikPlainText] LANGUAGE 'English') KEY INDEX [PK_TajikPoems] ON ([TajikPoemPlainTextCatalog], FILEGROUP [PRIMARY]) WITH (CHANGE_TRACKING = AUTO, STOPLIST = SYSTEM) Exports the Tajik (Cyrillic transliteration) overlay data — GanjoorTajikPoet/Cat/Poem/Verse — to its own git-tracked JSON repository, separate from the main ganjoor-data export. Kept separate deliberately: Tajik translation coverage is sparse (most poets/categories/ poems have no Tajik counterpart at all), the consumer audience is different (Cyrillic Tajik apps, not general Ganjoor API consumers), and update cadence is independent of the main Persian corpus. This export only ever writes entries that actually have a Tajik database record; everything else is skipped. Ids and FullUrls match the main ganjoor-data repo exactly, so a consumer resolves structure/metre/sections for a poem by fetching the same id/path from there and merging verses by VOrder. batch-loads everything Tajik for this poet (mirrors the same "load once per poet, not once per poem" fix applied to the main export) before walking the tree walks the *main* category tree (for structure/ordering — Tajik tables have no tree shape of their own) but only ever writes a file where a Tajik counterpart actually exists; still recurses into every child regardless, since a deeper category can have a translation even when its parent doesn't. regenerate TOCs start generating sub cats TOC Get List of poets get poet by id get poet by url poet image id by url get cat by url get list of books generate missing book covers get cat by id get page url by id clean cache for paeg by id clean cache for page by url clean cache for page by comment get redirect url for a url get page by url get poem recitations (PlainText/HtmlText are intentionally empty) get poem whole sections get user up votes for the recitations of a poem get poem comments get a single comment information (replies are not included) new comment update user's own comment link or unlink user's own comment to a coupletIndex if null then unlinks couplet summary delete a reported comment publish awaiting comment delete anybody's comment delete user own comment get recent comments report a comment id of report record delete a report Get list of reported comments get poem images by id (some fields are intentionally field with blank or null), EntityImageId : the most important data field, image url is {WebServiceUrl.Url}/api/images/thumb/{EntityImageId}.jpg or {WebServiceUrl.Url}/api/images/norm/{EntityImageId}.jpg Get Poem By Url get poem verses Get Poem By Id sections delete unreviewed user corrections for a poem send poem correction last unreviewed user correction for a poem get user or all corrections if sent empty returns all corrections effective corrections for poem get correction by id get next unreviewed correction unreviewed corrections count random poem id from hafez sonnets and old c.ganjoor.net service get a random poem from hafez Get Similar Poems accroding to prosody and rhyme informations language tagged poem sections Search You need to run this scripts manually on the database before using this method: CREATE FULLTEXT CATALOG [GanjoorPoemPlainTextCatalog] WITH ACCENT_SENSITIVITY = OFF AS DEFAULT CREATE FULLTEXT INDEX ON [dbo].[GanjoorPoems]( [PlainText] LANGUAGE 'English') KEY INDEX [PK_GanjoorPoems]ON ([GanjoorPoemPlainTextCatalog], FILEGROUP [PRIMARY]) WITH (CHANGE_TRACKING = AUTO, STOPLIST = SYSTEM) modify page modify poem => only these fields: NoIndex, RedirectFromFullUrl, MixedModeOrder break a poem from a verse forward update related sections return page modifications history get old version returns metre list (ordered by Rhythm) modify poet create new poet delete poet delete a page delete a poem delete a category chaneg poet image find poem rhyme find poem section rhyme find poem rhythm manually add a duplicate for a poems delete duplicate get category poem related images add page send cat correction delete unreviewed user corrections for a cat last unreviewed user correction for a cat get user or all corrections for categories if sent empty returns all corrections cat effectinve corrections get cat correction by id get next unreviewed cat correction unreviewed cat corrections count aggressive cache Database Context Configuration Background Task Queue Instance IAppUserService instance Messaging service Image File Service IMemoryCache http client options service constructor translation service implementation add language update an existing language delete language get langauge by id get all languages Database Context constructor geo location service implementation add new location update an existing location delete location get location by id get all locations Database Context constructor Persian specific text utility class convert persian text to simple pinglish equaivalnet for persian characters Language Utilitis Persian Alphabet English Alphabet Check to see if input Contains Only Specific Characters make text searchable simple, best-effort Perso-Arabic -> Tajik Cyrillic letter substitution. NOT a linguistically correct transliteration: Perso-Arabic script does not write short vowels, so a letter-by-letter map cannot recover them (that's why a dedicated tool like persian-tajik.ir exists, and why the bulk of TajikGanjoor's content comes from real TajikPoems/TajikCats/TajikPoets rows rather than on-the-fly conversion). This exists only as a fallback for spots - like a neighbouring poem's excerpt on the next/previous nav - that don't have a proper Tajik translation in the database yet, so at least something in Cyrillic is shown instead of raw Perso-Arabic script. find rhyme format money format datetime short date Music Catalgue Service Implementation get golha collection programs get golha program tracks Configuration Database Contetxt constructor manipulating picture files recover from files add new picture pass empty if you want a generic date based folder returns image info Rotate Image in 90 deg. multiplicants: 90, 180 or 270 replace an image find image encodder returns image file stream فرمت تصاویر بندانگشتی و ... image/jpeg عرض تصویر با اندازه نرمال - جهت نمایش در صفحات /// طول تصویر با اندازه نرمال - جهت نمایش در صفحات عرض تصویر بندانگشتی طول تصویر بندانگشتی image info cache Image Storage Path Generated Cropped Image Based On ThumbnailCoordinates For Notes Database Contetxt Configuration Simple Image Storage (not storing multiple image sizes) for creating small cropped images memory cache constructor poet photo suggestion service return list of suggested photos for a poet returns a single suggested photo suggest a new photo for a poet next unpublished suggested photo for poets unpublished suggested photos count for poets modify a suggested photo for poets delete a suggested photo for poets delete published suggested photo Database Context picture file service Messaging service IAppUserService instance Configuration constructor Queued FTP Upload Service Implementation add upload (you should call ProcessQueue manually) s process queue reset queue get queued ftp uploads Database Context Configuration Background Task Queue Instance constructor creates an RSS feed from recitations build rss Role Service Implementation gets list of SecurableItem, should be reimplemented in end user applications constructor Best-effort — see the implementation for why this should never throw in a way the caller needs to handle. Deliberately NOT a GanjoorService partial, unlike everything else in this project. EmbeddingIndex (~530MB in memory) and QueryEmbedder (a loaded ONNX model) both need to be true singletons — constructed once at startup, never per-request — while GanjoorService and RMuseumDbContext are scoped per-request throughout this codebase. Injecting a singleton's dependencies into a per-request class (or vice versa) is a real DI lifetime bug, not just an inconsistency, so this stays a separate service registered as a singleton itself (see INTEGRATION.md for the exact registration). Depends on LazySemanticSearchResources rather than EmbeddingIndex/QueryEmbedder directly — deliberately, after a production incident where eager, throwing DI factories for those two meant a load failure (wrong/missing file paths) prevented GanjoorController itself from being constructed, taking down every endpoint under /api/ganjoor with a 503, not just semantic search. This class must never let a resource-loading failure become an unhandled exception that propagates past SearchAsync — see the catch below. LazyQueryScopeIndex is a SEPARATE lazy singleton from LazySemanticSearchResources on purpose — poet/category name detection ("در کدام شعر حافظ") is a genuinely independent concern from the embedding/model loading, with its own independent failure mode; a bug in one must not be able to disable the other. ganjoor-data's own editing workflow requires this exact prefix be removed once a human has reviewed/edited a poem's summary — its continued presence is a direct, already-existing signal for "not yet human-reviewed," not something this project invented or has to infer. Common Persian function words stripped out before keyword-matching a query against verse text (see SelectPreviewVerses) — the kind of words that appear in nearly every query regardless of topic ("شعری در مورد ... پیدا کن") and would otherwise match almost any couplet in almost any poem, defeating the whole point of looking for a RELEVANT couplet rather than an arbitrary one. Not remotely exhaustive Persian stopword coverage — just the words that actually show up in how people phrase this kind of query, extended as real queries reveal gaps. Splits the query on whitespace (deliberately NOT on ZWNJ — "بی‌وفایی" should survive as one token, not fracture into "بی" + "وفایی", where "بی" alone is a common enough prefix to false-positive-match all over the place), strips surrounding punctuation, drops stopwords and anything too short to be a meaningful keyword on its own. Looks for the couplet (Right/Left verse pair) whose combined text contains the most query keywords, and returns it (plus, if there's room within previewVerseCount, the couplet immediately following it, for a little reading continuity rather than a single isolated pair). Falls back to the poem's opening verses — the previous, always-the-same-lines behavior — if no keyword appears anywhere in the scanned verses, or if there were no real keywords to search for at all (a query that was entirely stopwords, or empty after stripping them). Matches against CoupletSummary (when present) as well as the raw verse text — the summary is clean, modern-language prose, while the verses themselves are archaic and metaphorical and often won't literally contain a query's keywords even when the couplet is genuinely on-topic. CoupletSummary is only ever read from the FIRST verse of the pair (the "Right"/anchor position) — ganjoor-data stores it once per couplet there, never on the second ("Left") verse; a small number of "Left" rows do carry a stray value (a data anomaly, not a second legitimate copy), and this deliberately never reads it from that position, matching how the data is actually meant to be laid out rather than how a few rows happen to look. Walks the whole category subtree rooted at rootCatId (breadth-first, level by level) and returns every category id in it, including rootCatId itself. Needed because a detected "book" category (شاهنامه, غزلیات, ...) is rarely where poems live directly — it's typically broken into many nested subcategories, with the actual poems several levels deeper. One query per depth level, not per node — a large book with many subcategories still only costs as many round-trips as the tree is deep, not how many nodes it has. ganjoor.net banners service Add site banner modify site banner delete site banner get site banners get a random site banner Database Context Image File Service IMemoryCache constructor User Visits Tracking Service Implementation add record previous visit date/time if any delete record start or stop tracking user get user history options service Database Context constructor Audio Narration Service Implementation returns list of narrations send Guid.Empty if you want all narrations returns list of publish recitations get category top one recitations check if a category has any recitations get published recitation by id return selected narration information Delete recitation Replace only the synchronization (xml) file of an existing, approved recitation owned by the user. The recitation's mp3 file is left untouched. The uploaded xml's embedded PoemId and audio file checksum must match the target recitation's GanjoorPostId and Mp3FileCheckSum respectively (i.e. it must be a resync of the very same, already uploaded, mp3 file) or the request is rejected - this method never accepts a new mp3, only a corrected/retimed xml for the existing one. On success the updated xml (and the unchanged mp3) are re-published to the external/backup FTP servers in the background and the user is notified of the final result (success or failure), reusing the exact same publishing/notification pipeline used for normal recitation uploads. must be the recitation owner Gets Verse Sync Range Information narration id validate PoemNarrationViewModel updates metadata for narration build profiles from exisng narrations data User Id which becomes owner of imported data error string if occurs Initiate New Upload Session for audio Save uploaded file finalize upload session (add files) Moderate pending narration retry publish unpublished narrations Get Upload Session (including files) Get User Profiles Get User Default Profile validating narration profile Add a narration profile Update a narration profile Delete a narration profile Get uploads descending by upload time if userId is empty all user uploads would be returned publishing tracker data Transfer Recitations Ownership move recitaions of an artist to the first position Synchronization Queue report an error in a recitation get errors reported for recitations reject a reported error for recitations and notify the reporter (and deletes the report) accepts a reported error for recitations and notify the reporter and recitation owner (and deletes the report) accepts a reported error for recitations, add mistake to approve the mistake and notify the reporter and recitation owner (and deletes the report) remove approved mistake edit approved mistake text compute poem recitations order up vote a recitation recitation id revoke recitaion up vote recitaion id switches recitation upvote upvote status get user upvoted recitations check recitaions with missing files and add them to reported errors list Upload Enabled (temporary switch off/on for upload) Configuration Database Contetxt Background Task Queue Instance Messaging service Users service memory cache ganjoor service constructor Music Catalogue Service get golha collection programs get golha program tracks manipulating picture files add new picture pass empty if you want a generic date based folder recover from files returns image info returns image file stream Rotate Image in 90 deg. multiplicants: 90, 180 or 270 replace an image Get Encoder Generate Cropped Image Based On ThumbnailCoordinates For Notes Image Storage Path poet photo suggestion service; return list of suggested photos for a poet returns a single suggested photo suggest a new photo for a poet next unpublished suggested photo for poets unpublished suggested photos count for poets modify a suggested photo for poets delete a suggested photo for poets delete published suggested photo Queued FTP Upload Service add upload (you should call ProcessQueue manually) s process queue reset queue get queued ftp uploads Audio Narration Service returns list of narrations send Guid.Empty if you want all narrations returns list of publish narrations (if poetId or catId is non-zero its ordered by poemId ascending if not it is ordered by publish date descending) get category top recitations check if a category has any recitations get published recitation by id return selected narration information Delete recitation (recitation should belong to userId) Replace only the synchronization (xml) file of an existing, approved recitation owned by the user. The recitation's mp3 file is left untouched; the uploaded xml's embedded PoemId and audio checksum must match the target recitation or the request is rejected. On success, the updated xml is re-published to the external/backup FTP servers in the background and the user is notified of the final result (success or failure). must be the recitation owner Gets Verse Sync Range Information narration id updates metadata for narration build profiles from exisng narrations data User Id which becomes owner of imported data error string if occurs Initiate New Upload Session for audio Save uploaded file finalize upload session (add files) Moderate pending narration Get Upload Session (including files) Get User Profiles Get User Default Profile Add a narration profile Update a narration profile Delete a narration profile Transfer Recitations Ownership Get uploads descending by upload time if userId is empty all user uploads would be returned publishing tracker data move recitaions of an artist to the first position Synchronization Queue report an error in a recitation get errors reported for recitations reject a reported error for recitations and notify the reporter (and deletes the report) accepts a reported error for recitations, change status of the recitation to rejected and notify the reporter and recitation owner (and deletes the report) accepts a reported error for recitations, add mistake to approve the mistake and notify the reporter and recitation owner (and deletes the report) remove approved mistake edit approved mistake text compute poem recitations order up vote a recitation recitation id revoke recitaion up vote recitaion id switches recitation upvote upvote status get user upvoted recitations check recitaions with missing files and add them to reported errors list retry publish unpublished narrations Upload Enabled (temporary switch off/on for upload) Add site banner modify site banner delete site banner get site banners get a random site banner User Visits Tracking Service add record delete record start or stop tracking user get user history comment ranking score calculator ganjoor poem tools get an excerpt for the poem a legal music streaming/store platform accepted for poem music track links stable identifier display name (Persian) 16px icon path, null when no artwork is available yet 32px icon path, null when no artwork is available yet oEmbed endpoint accepting a ?url= parameter, null when the platform has none accepted hosts, each matching itself and its subdomains accepted host pattern, used where the host varies by country does the given lowercased host belong to this platform? validates user supplied music links against an allow-list of legal music platforms maximum accepted url length accepted platforms, ordered so that more specific hosts win (YouTube Music before YouTube) IconUrl/LargeIconUrl are only set for platforms whose artwork already exists under GanjooRazor/wwwroot/image; drop a {Key}16.png/{Key}.png pair in image/music and fill them in. query parameters dropped during normalization so that the same track always yields the same url comma separated platform names, for user facing messages validate a user supplied music url and rewrite it to its canonical form Persian error message when validation fails platform of an already stored url, null when it belongs to none of the accepted platforms Writes JSON in a way that is stable across runs: fixed indentation, LF line endings, UTF-8 without BOM, a single trailing newline, and Persian/Arabic text left unescaped (the default encoder \u-escapes non-ASCII, which is both hard to review in a diff and bloats file size for a mostly-Persian dataset). Callers are responsible for sorting any collections before serializing — this class only guarantees stable *encoding*, not stable *ordering*, since ordering is a data decision. Files are only actually written to disk when the content differs from what's already there, so an unmodified poem never shows up in `git status` even if the whole tree is regenerated every run. Serializes and writes it to only if the resulting bytes differ from the file's current content. Creates parent directories as needed. Returns true if the file was created or changed, false if it was already up to date. Options for publishing the public export tree to a git remote. Bind this from the "PublicDataExport" (or "TajikPublicDataExport") configuration section. local working copy path (e.g. C:\ganjoor-public-data or /var/ganjoor/public-data) remote URL, e.g. https://github.com/ganjoor/ganjoor-data.git if false, everything is written and committed locally but never pushed — useful for a first dry run before wiring up real credentials git username for the push (for GitHub, a PAT is used as both username-independent and as the token below — GitHub only checks the token) personal access token / app token with push rights to RemoteUrl. Keep this in user-secrets / environment variables, never committed to appsettings.json. Full path to git.exe, e.g. "C:\Program Files\Git\cmd\git.exe". Optional — leave empty to resolve "git" via PATH (with a fallback to common Git-for-Windows install locations if that fails, see GitRepoPublisher.ResolveGitExecutable). Set this explicitly if the process running this job has a PATH that doesn't include git for any reason (e.g. a Windows service running under a service account with its own PATH). how long a single git command is allowed to run before it's killed and the job fails with a clear timeout error, instead of hanging forever (e.g. if git ever ends up waiting on an interactive prompt with no terminal to answer it — see GIT_TERMINAL_PROMPT in GitRepoPublisher) Thin wrapper around the native git CLI (not LibGit2Sharp) for the export job: sync a local working copy, stage, commit, push. This shells out to git.exe deliberately rather than using LibGit2Sharp's own transport. LibGit2Sharp's built-in HTTP push turned out to be unreliable at this job's actual scale (hundreds of thousands of small files, a correspondingly large push) — it repeatedly failed mid-push with "error receiving data from socket: An existing connection was forcibly closed by the remote host", a transport-level failure. The native git CLI (what every other git client actually uses) handles large pushes far more robustly, so this class runs it as a subprocess instead. Requires Git for Windows (or any git install) to be on PATH — the same requirement as running `git` from a normal command prompt. The auth token is passed only via a one-off `-c http.extraHeader=...` argument on whichever single invocation needs it (clone/fetch/push) — it's never written into .git/config, so it never touches disk. Ensures the local working copy exists and is up to date with the remote before the export job starts writing files into it. Clones on first run, fetches + hard-resets to the remote branch on subsequent runs (this working copy is bot-owned; nobody should be hand-editing it, so a hard reset is safe and keeps the job idempotent). A .git/index.lock left behind by an abruptly-terminated git process (e.g. an app pool recycle, or a previous run of this same job that got killed mid-command rather than finishing) blocks every future git command in this working copy with a confusing "Another git process seems to be running" error, even when nothing actually is. GanjoorService's TryStartExclusiveExportJob already guarantees only one run of a given export job (main or Tajik) executes at a time *within this process* — so reaching this method at all means the current call is the sole legitimate owner of this working copy right now, and any lock file found here can only be a leftover from something that is no longer running (that in-process guard doesn't survive an app pool/process restart, which is exactly the scenario that leaves an orphaned lock file in the first place). Safe to remove unconditionally on that basis. Stages every change under the working copy and, if anything actually changed, commits and (if enabled) pushes. Returns the number of files touched (0 means nothing changed since last run — a normal, expected outcome on most nightly runs). Resolves which git executable to launch, cached for the lifetime of this instance. Prefers an explicitly configured path; otherwise tries "git" (PATH resolution via Process/CreateProcess) and, only if that file genuinely doesn't exist anywhere on PATH, falls back to checking the usual Git-for-Windows install locations directly — covers the common case where the process that's running this job has a stale PATH snapshot from before git was installed (e.g. Visual Studio/IIS Express started before the PATH environment variable was updated). checks every directory on this process's own PATH for the given executable, without actually starting it — used only to decide whether to bother trying the Git-for-Windows fallback locations keeps the auth token out of exception messages that might end up logged somewhere A bare numeric id (poem id, category id, ...) is meaningless to a static file tree unless something maps it to a path. Writing one giant id->path file doesn't scale to Ganjoor's poem count, so ids are bucketed by id / shardSize into small shard files a client can compute the name of directly — no lookup-before-the-lookup needed. Writes every id in to poets-by-id.json (or configured file name) with no sharding — for tables small enough that one file is fine (e.g. poets: a few hundred rows). Writes as bucketed shard files under /index/{category}-by-id/{bucket}.json, where bucket = id / shardSize. Only buckets that actually contain ids get a file — an empty bucket produces no request-able file, which is fine since a client only ever asks for the bucket of an id it already has. Belt-and-suspenders check on top of the allowlist design: even though the export DTOs only ever declare the fields we explicitly want published, this scans every DTO type in the RMuseum.Models.Ganjoor.PublicExport namespace by reflection and throws if a property name or type looks like it could carry personal data. Call this once at application startup (Development/CI) and/or from a test — see AssertSafe(). property-name patterns that must never appear on an export DTO Scans every public class in the PublicExport DTO namespace. Throws InvalidOperationException naming the offending type/property if anything trips the checks. Same no-op-if-unchanged, LF/UTF-8-no-BOM behavior as , for plain-text files (currently just the generated API.md) rather than JSON. Abstracts "read a relative path from the public data export" so the importer doesn't care whether it's reading a local `git clone` or fetching over HTTP from a CDN. Returns the file's text content, or null if it doesn't exist at this path (a missing file is a normal, expected outcome — e.g. a leaf category has no children). Reads from a local folder — the expected path for a developer who already ran `git clone` on the public data repo, which is both the faster option and the one that doesn't put load on jsDelivr/GitHub for a full-corpus import. Fetches over HTTP — point baseUrl at either the jsDelivr CDN URL (https://cdn.jsdelivr.net/gh/ORG/REPO@main/) or raw.githubusercontent.com. A missing file (404) is treated the same as "doesn't exist", not an error. Loads the published embeddings.f32 (raw N x D float32, row-major, sorted by poem id ascending per the export's own writer) + embeddings-index.json into memory once, and does brute-force cosine similarity search against them. Brute force, not a vector database, is a deliberate choice at this scale — ~130k poems x 1024 dimensions is ~530MB, comfortably held in RAM, and a full linear scan per query is fast enough (low tens of milliseconds) that a dedicated vector index/database would be solving a problem this corpus doesn't actually have. Revisit only if the corpus grows by an order of magnitude or query volume becomes very high. Thread-safety: immutable after Load() — every field is only ever written once, during loading, before this instance is published to any other thread (e.g. via DI singleton registration). Safe for concurrent reads (searches) from multiple requests afterward. Loads embeddings.f32 + embeddings-index.json from the given directory. Intended to be called once at startup (e.g. from a DI factory) — this reads and holds ~530MB in memory, not something to redo per-request. Returns the topK poem ids most similar to queryVector, ranked descending by cosine similarity. queryVector must already be the SAME dimension as this index and, for the score to mean what it claims (a true cosine similarity), should already be L2-normalized the same way the indexed vectors are — see QueryEmbedder. If allowedPoemIds is provided (non-null), only poems in that set are eligible — everything else is scored as excluded and can never appear in the results, however similar it might be. Used for scoped search ("در کدام شعر حافظ" -> restrict to Hafez's poems) — still a full scan either way, just with cheap early-outs for excluded rows, since this corpus is small enough that a full scan is fast regardless. Same "load once, never throw" pattern as LazySemanticSearchResources, but deliberately a SEPARATE singleton with its own independent failure domain: if the poet/category name lookup fails to load for any reason, that must only disable scope auto-detection ("در کدام شعر حافظ" -> unscoped, searches everything) — it must never affect the embedding index/model loading or plain (unscoped) search, which are a completely different concern. Uses a SemaphoreSlim rather than a plain `lock`, since the actual load is async (DB queries) and you can't `await` inside a `lock` block. Loads (once) and returns the scope index, or null if loading failed or hasn't succeeded yet — NEVER throws. The passed-in context is only actually used on the first call that does real work; a context created by the caller for its own SearchAsync call is reused here rather than this class creating its own, since it's only needed for the one-time load. Wraps EmbeddingIndex + QueryEmbedder loading so a failure (missing files, wrong paths, corrupt data) can NEVER take down anything else in the app. The original design registered EmbeddingIndex/QueryEmbedder as singletons whose DI factories called EmbeddingIndex.Load(...)/`new QueryEmbedder(...)` directly — both of which throw on failure. Because GanjoorController's constructor (indirectly, through ISemanticSearchService) depended on them, a load failure meant the controller itself couldn't be constructed — taking down EVERY endpoint under /api/ganjoor, not just semantic search, with a 503. That's exactly what happened in production. This class exists so that can't happen again: the actual load is deferred to first real use (not app/controller construction), attempted at most once, and a failure is caught, logged, and remembered — SearchAsync() then reports "search unavailable" as an ordinary result, not an exception that propagates into breaking anything else. Also worth knowing if this server runs multiple IIS worker processes for the same app pool: each process gets its own instance of this (and everything it loads) — "singleton" only means one instance per process, not per server. See the migration notes for the memory math this implies at scale. SemanticSearch:Enabled gates ALL of the above, checked before anything else runs — this is what actually keeps a disabled instance (e.g. api.ganjoor.net, where third-party apps shouldn't get this feature) from ever loading the ~530MB embeddings file or the ONNX model into memory at all, not just from serving requests. Defaults to disabled (fail-closed) if the key is missing entirely — deliberately, since the whole point of this flag is preventing resource loading on instances that shouldn't have it; an instance that's SUPPOSED to have it (ganjgah.ir) needs the key explicitly set to "True", not left implicit. A gentle re-ranking multiplier applied to results whose PoemSummary is still AI-generated and un-reviewed (detected by the "هوش مصنوعی:" prefix ganjoor-data's own editing workflow requires removing once a human has reviewed/edited a summary — see SemanticSearchService for how this is actually applied). A soft nudge, not a filter: ~95% of summaries currently carry this prefix, so excluding them outright would gut coverage for most queries. Configurable (SemanticSearch:AiSummaryScorePenalty) rather than hardcoded, since the right effect size here is a judgment call worth tuning without a redeploy. Read here (not directly in SemanticSearchService) purely to reuse the config-reading this class already does — this value has nothing to do with the lazy-loaded embeddings/model themselves and is available even when Enabled is false. Attempts to load the resources on first call (subsequent calls reuse the same result, success or failure — this never retries automatically; a fresh app start is required to try again after a config/file fix, which is the expected deploy-and-restart flow anyway). Returns true and populates both out parameters if available; returns false and populates otherwise. NEVER THROWS — that guarantee is the entire point of this class. If SemanticSearch:Enabled isn't "True", this returns false immediately, every call, without ever touching EmbeddingIndex.Load/QueryEmbedder's constructor — the actual mechanism that keeps a disabled instance from loading anything into memory at all. Thrown by SemanticSearchService when the underlying resources aren't available — the controller catches this specifically and returns HTTP 503 with the message, distinct from a plain 400/500, so a client (or a person checking logs) can tell "this feature isn't configured/loaded right now" apart from "the query itself was bad" or "something crashed". Embeds a single user-typed search query using the SAME ONNX model + tokenizer as scripts/generate_embeddings.py (the Python side that indexed the poem corpus) — this is non-negotiable: a query embedded in a different space than the documents it's being compared against produces meaningless similarity scores, silently (no error, just bad results), not something that shows up as a crash. Tokenizer construction status: confirmed via reflection against the actual installed Microsoft.ML.Tokenizers package that BpeOptions.ByteLevel is the correct mechanism for non-Latin scripts (two earlier attempts that only changed the pre-tokenizer, not this flag, both ran without error but still silently tokenized Persian to zero tokens). Still needs a real local-harness run to confirm this specific configuration works end-to-end — "confirmed the right property exists" isn't the same as "confirmed this combination is exactly right" — but this is grounded in the real API now, not a documentation guess. Separately, a local console-app test (same code, real model files, run on a Mac) already confirmed the ONNX inference call itself (building the KV-cache tensors below and calling session.Run) does NOT crash on that hardware — good evidence the tensor construction is basically sound, though it doesn't rule out an environment-specific crash on the actual production OS/hardware, which hasn't been re-tested since the tokenizer fix. See VERIFICATION.md for the paired Python/C# tokenizer comparison — do this before trusting actual search *results*, separate from "does it run without crashing." Query-time embedding is always exactly one query per call (a person typing into a search box), never a batch — unlike the Python indexing script, which batched many poems together and needed padding logic. That simplifies this class: no padding, no batch-dimension complexity, last-token pooling reduces to simply the final sequence position (no attention-mask lookup needed, since there's no padding to skip past). Per Qwen3-Embedding's documented convention, QUERIES (unlike documents) benefit from an instruction prefix. generate_embeddings.py deliberately does NOT add one to documents (poem summaries) — this asymmetry is the model's own documented design, not an inconsistency. If this template ever changes, or if it's ever added to/removed from the document side, both sides must be updated together, or queries and documents drift into subtly different embedding spaces. This exact wording hasn't been benchmarked for Persian poetry specifically — reasonable to A/B test once the basic pipeline is confirmed working (see VERIFICATION.md), not something to treat as final. Returns an L2-normalized embedding for queryText, comparable via dot product against EmbeddingIndex's vectors (which are normalized the same way). Detects when a free-text query names a specific poet and/or book/collection (e.g. "در کدام شعر حافظ" -> حافظ, "در کدام بخش شاهنامه" -> شاهنامه) so search can be scoped to just that poet/category instead of the whole corpus. Deliberately simple substring matching, not real NLP/NER — good enough for the common, unambiguous case (a poet's distinctive nickname, or a book title effectively unique to one poet, like شاهنامه), and safely conservative for the ambiguous case: a generic category title shared by many poets (غزلیات appears for most of them) is only used as a scope if a specific poet was ALSO named in the same query, narrowing which one is meant — otherwise it's dropped rather than guessing which poet's غزلیات the person meant. url جهت ذخیره، دریافت و پردازش مخازن مجموعه های شعرها به کار می رود ذخیرۀ لیستی از GDBInfoها در یک فایل xml دریافت یک فایل xml و تبدیل آن به لیستی از GDBInfoها اطلاعات فایل صوتی شعر سازنده شناسۀ شعر شناسۀ فایل صوتی با شروع از 1 برای هر شعر مسیر فایل شرح آیا همگام شده ساختار اطلاعات همگام سازی ترتیب مصرع (-1 نشانگر آغاز فایل صوتی و -2 نشانگر پایان آن است) زمان در فایل صوتی بر حسب میلی ثانیه اطلاعات همگام سازی نشانی دریافت فایل صوتی آیا لینک دریافت فایل صوتی مستقیم است شناسۀ یکتای اطلاعات همگام سازی امضای یکتای فایل صوتی محاسبه چک سام فایل آیا اطلاعات همگام سازی آپلود شده؟ تبدیل به رشته شرح Warning: not always filled Warning: Rarely filled پردازشگر ذخیره و بارگذاری لیستهای اشعار ذخیره در فایل ذخیره در فایل بارگذاری از فایل