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
پردازشگر ذخیره و بارگذاری لیستهای اشعار
ذخیره در فایل
ذخیره در فایل
بارگذاری از فایل