report.typ 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // ==========================================
  2. // PROFILE: НАУЧНО-ТЕХНИЧЕСКИЙ ОТЧЁТ
  3. // ==========================================
  4. #import "../../domain/document.typ": document-profile, fail
  5. #import "../../domain/attachments.typ": attachment-set
  6. #import "../../report.typ": report
  7. #import "../../appendices.typ": make_appendices
  8. #import "../references.typ": render-bibliographies
  9. #let placeholder-path(kind) = {
  10. if kind == "signature" {
  11. path("../../assets/placeholders/signature.svg")
  12. } else if kind == "stamp" {
  13. path("../../assets/placeholders/stamp.svg")
  14. } else {
  15. path("../../assets/placeholders/logo.svg")
  16. }
  17. }
  18. #let report-company-summary(company) = {
  19. let summary = company.contacts.at("summary", default: none)
  20. if summary != none {
  21. summary
  22. } else {
  23. let lines = (company.legal.name,)
  24. let ids = ()
  25. for key in ("inn", "kpp", "ogrn", "bin", "kbe") {
  26. let value = company.legal.at(key, default: none)
  27. if value != none { ids.push(upper(key) + " " + value) }
  28. }
  29. if ids.len() > 0 { lines.push(ids.join(" ")) }
  30. for key in ("address", "phone", "email", "website") {
  31. let value = company.contacts.at(key, default: none)
  32. if value != none { lines.push(value) }
  33. }
  34. lines.join("\n")
  35. }
  36. }
  37. #let resolve-signing-resource(resource, kind, ctx) = {
  38. if ctx.options.mode != "final" {
  39. none
  40. } else if resource != none {
  41. resource
  42. } else if ctx.options.at("media-policy") == "placeholder" {
  43. placeholder-path(kind)
  44. } else {
  45. none
  46. }
  47. }
  48. #let render-report-attachments(attachments) = {
  49. if attachments.items.len() > 0 {
  50. make_appendices([
  51. #for item in attachments.items [
  52. // Метка для ссылок из глав: #ref(<app-...>, supplement: [приложении]).
  53. #if item.at("label", default: none) != none [
  54. #heading(level: 1, outlined: item.outlined)[#item.title] #item.at("label")
  55. ] else [
  56. #heading(level: 1, outlined: item.outlined)[#item.title]
  57. ]
  58. #if item.subtitle != none {
  59. align(center)[#text(weight: "bold")[#item.subtitle]]
  60. v(0.5cm)
  61. }
  62. #if type(item.body) == function { (item.body)() } else { item.body }
  63. ]
  64. ])
  65. }
  66. }
  67. #let normalize-report(metadata) = {
  68. if type(metadata.attachments) == array {
  69. metadata.insert(
  70. "attachments",
  71. attachment-set(items: metadata.attachments, numbering: "cyrillic"),
  72. )
  73. }
  74. metadata
  75. }
  76. #let validate-report(metadata) = {
  77. if type(metadata.title) != str or metadata.title.trim() == "" {
  78. fail("report.title", "название отчёта обязательно")
  79. }
  80. if type(metadata.year) != int {
  81. fail("report.year", "ожидалось целое число")
  82. }
  83. if type(metadata.executors) != array {
  84. fail("report.executors", "ожидался array")
  85. }
  86. if type(metadata.attachments) != dictionary or metadata.attachments.at("kind", default: none) != "attachment-set" {
  87. fail("report.attachments", "ожидался attachment-set")
  88. }
  89. metadata
  90. }
  91. #let render-report(body, ctx) = {
  92. let company = ctx.company
  93. let data = ctx.metadata
  94. let mode = if ctx.options.mode == "final" { "default" } else { "draft" }
  95. let watermark = if ctx.options.mode == "draft" and ctx.options.watermark == none {
  96. "DRAFT"
  97. } else {
  98. ctx.options.watermark
  99. }
  100. let signature = resolve-signing-resource(company.resources.signature, "signature", ctx)
  101. let stamp = resolve-signing-resource(company.resources.stamp, "stamp", ctx)
  102. let logo = if company.resources.logo != none {
  103. company.resources.logo
  104. } else if ctx.options.at("media-policy") == "placeholder" {
  105. placeholder-path("logo")
  106. } else {
  107. none
  108. }
  109. let composed = [
  110. #body
  111. #if ctx.bibliographies.len() > 0 {
  112. pagebreak()
  113. render-bibliographies(ctx.bibliographies)
  114. }
  115. #if ctx.attachments.items.len() > 0 {
  116. render-report-attachments(ctx.attachments)
  117. }
  118. ]
  119. report(
  120. composed,
  121. company_info: report-company-summary(company),
  122. udk: data.udk,
  123. director_company: company.director.title + " " + company.legal.name,
  124. director_name: company.director.name,
  125. director_date: data.at("director-date"),
  126. sign_image: signature,
  127. stamp_image: stamp,
  128. logo_image: logo,
  129. is_research: data.at("is-research"),
  130. is_intermediate: data.at("is-intermediate"),
  131. stage_num: data.at("stage-number"),
  132. vol_num: data.at("volume-number"),
  133. title: data.title,
  134. theme: data.theme,
  135. contract_num: data.at("contract-number"),
  136. contract_date: data.at("contract-date"),
  137. city: data.city,
  138. year: data.year,
  139. executors: data.executors,
  140. figure-before: data.at("figure-before"),
  141. figure-after: data.at("figure-after"),
  142. table-before: data.at("table-before"),
  143. table-after: data.at("table-after"),
  144. figure-caption-before: data.at("figure-caption-before"),
  145. figure-caption-after: data.at("figure-caption-after"),
  146. table-caption-before: data.at("table-caption-before"),
  147. table-caption-after: data.at("table-caption-after"),
  148. report_mode: mode,
  149. watermark: watermark,
  150. )
  151. }
  152. #let report-profile(
  153. title: "Название работы",
  154. theme: none,
  155. udk: none,
  156. director_date: none,
  157. is_research: false,
  158. is_intermediate: false,
  159. stage_number: none,
  160. volume_number: none,
  161. contract_number: none,
  162. contract_date: none,
  163. city: "Екатеринбург",
  164. year: datetime.today().year(),
  165. executors: (),
  166. attachments: attachment-set(numbering: "cyrillic"),
  167. bibliographies: (),
  168. figure_before: 0.75em,
  169. figure_after: 0.75em,
  170. table_before: 0.75em,
  171. table_after: 0.75em,
  172. figure_caption_before: 0pt,
  173. figure_caption_after: 0pt,
  174. table_caption_before: 0pt,
  175. table_caption_after: 0pt,
  176. ) = {
  177. document-profile(
  178. "report",
  179. render-report,
  180. metadata: (
  181. title: title,
  182. theme: theme,
  183. udk: udk,
  184. director-date: director_date,
  185. is-research: is_research,
  186. is-intermediate: is_intermediate,
  187. stage-number: stage_number,
  188. volume-number: volume_number,
  189. contract-number: contract_number,
  190. contract-date: contract_date,
  191. city: city,
  192. year: year,
  193. executors: executors,
  194. parties: (),
  195. attachments: attachments,
  196. bibliographies: bibliographies,
  197. figure-before: figure_before,
  198. figure-after: figure_after,
  199. table-before: table_before,
  200. table-after: table_after,
  201. figure-caption-before: figure_caption_before,
  202. figure-caption-after: figure_caption_after,
  203. table-caption-before: table_caption_before,
  204. table-caption-after: table_caption_after,
  205. ),
  206. normalize: normalize-report,
  207. validate: validate-report,
  208. )
  209. }