module content_gen; import plant_data; import dom_utils; import dxml.writer; import dxml.util; import std.stdio; import std.path; import std.array; import std.algorithm; import std.file; import std.conv; void renderHTML(PlantData data) { renderSpeciesCards(data); renderSpeciesPages(data); } private void injectContent(string filename, string startTag, string endTag, string newContent) { import std.file; string data = std.file.readText(filename); ptrdiff_t startIdx = indexOfStr(data, startTag); if (startIdx == -1) throw new Exception("Couldn't find start tag " ~ startTag); ptrdiff_t endIdx = indexOfStr(data, endTag); if (endIdx == -1) throw new Exception("Couldn't find end tag " ~ endTag); string prefix = data[0..(startIdx + startTag.length)]; string suffix = data[endIdx..$]; string newData = prefix ~ newContent ~ suffix; std.file.write(filename, newData); } private void renderSpeciesCards(PlantData data) { string tpl = std.file.readText(buildPath( "garden-data-gen", "templates", "species-card.html" )); Appender!string htmlApp; foreach (s; data.species) { string card = replaceAll(tpl, [ "!NAME!": s.name, "!SCIENTIFIC_NAME!": s.scientificName, "!DESCRIPTION!": s.description, "!LINK!": "garden/species/" ~ s.id ~ ".html", "!REF_LINK!": s.referenceLink, "!REF_LINK_TEXT!": s.referenceLink ]); ImageFilePair[] imagePairs = getSpeciesImages(s.id); if (imagePairs.length > 0) { card = replaceFirst(card, "!IMAGES!", generatePhotoSwipeElement(imagePairs[0], false)); } else { card = replaceFirst(card, "!IMAGES!", ""); } htmlApp ~= "\n" ~ card; } injectContent( "garden.html", "", "", htmlApp[] ); } private void renderSpeciesPages(PlantData data) { string tpl = std.file.readText(buildPath( "garden-data-gen", "templates", "species-page.html" )); string plantCardTpl = std.file.readText(buildPath( "garden-data-gen", "templates", "plant-card.html" )); string speciesPagesDir = buildPath("garden", "species"); if (!exists(speciesPagesDir)) mkdirRecurse(speciesPagesDir); foreach (species; data.species) { Appender!string plantDivsApp; foreach (plant; data.plantsInSpecies(species)) { string card = replaceAll(plantCardTpl, [ "!IDENTIFIER!": plant.identifier, "!GENERATION!": "F" ~ plant.generation.to!string, "!DESCRIPTION!": plant.description, "!PLANTING_INFO!": plant.plantingInfo ]); ImageFilePair[] imagePairs = getPlantImages(plant.identifier); Appender!string imagesApp; foreach (imagePair; imagePairs) { imagesApp ~= generatePhotoSwipeElement(imagePair); } card = replaceFirst(card, "!IMAGES!", imagesApp[]); plantDivsApp ~= card; } string page = replaceAll(tpl, [ "!HEAD_TITLE!": species.name, "!PAGE_TITLE!": species.scientificName, "!ABOUT_TITLE!": "About " ~ species.name, "!ABOUT_TEXT!": species.description, "!REF_LINK!": species.referenceLink, "!REF_LINK_TEXT!": species.referenceLink, "!PLANTS_DIVS!": plantDivsApp[] ]); string pagePath = buildPath(speciesPagesDir, species.id ~ ".html"); std.file.write(pagePath, page); } } private string generatePhotoSwipeElement(ImageFilePair imagePair, bool addCaption = true) { import std.format; import std.datetime; import std.typecons; const linkFormat = ""; const thumbnailFormat = ""; const captionFormat = ""; string linkTag = format!(linkFormat)("../../" ~ imagePair.filename, imagePair.width, imagePair.height); string thumbnailTag = format!(thumbnailFormat)("../../" ~ imagePair.thumbnailFilename); Appender!string app; app ~= linkTag; app ~= "\n "; app ~= thumbnailTag; Nullable!DateTime imageTimestamp = getImageTimestamp(imagePair.filename); if (addCaption && !imageTimestamp.isNull) { DateTime dt = imageTimestamp.get; app ~= "\n "; app ~= format!(captionFormat)(dt.year, dt.month, dt.day, dt.date.toSimpleString); } app ~= "\n\n"; return app[]; } ptrdiff_t indexOfStr(string source, string target) { for (size_t i = 0; i < source.length - target.length; i++) { if (source[i..i+target.length] == target) { return i; } } return -1; } string replaceFirst(string source, string from, string to) { ptrdiff_t idx = indexOfStr(source, from); if (idx == -1) return source; string pre = idx == 0 ? "" : source[0..idx]; string post = source[idx+from.length..$]; return pre ~ to ~ post; } unittest { assert(replaceFirst("
!TEST
", "!TEST", "test") == "test
"); } string replaceAll(string source, string[string] values) { foreach (k, v; values) { source = replaceFirst(source, k, v); } return source; }