Test Util C0 Coverage Information - RCov

/home/masa/ywesee/oddb.org/src/util/oddbapp.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
/home/masa/ywesee/oddb.org/src/util/oddbapp.rb 1857 1800
87.94%
87.72%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 #!/usr/bin/env ruby
2 # OddbApp -- oddb -- hwyss@ywesee.com
3 
4 require 'odba'
5 require 'odba/index_definition'
6 require 'odba/drbwrapper'
7 require 'odba/18_19_loading_compatibility'
8 require 'custom/lookandfeelbase'
9 require 'util/currency'
10 require 'util/failsafe'
11 require 'util/ipn'
12 require 'util/oddbconfig'
13 require 'util/searchterms'
14 require 'util/session'
15 require 'util/updater'
16 require 'util/exporter'
17 require 'util/validator'
18 require 'util/loggroup'
19 require 'util/soundex'
20 require 'util/iso-latin1'
21 require 'util/notification_logger'
22 require 'util/ngram_similarity'
23 require 'util/today'
24 require 'models'
25 require 'commands'
26 require 'paypal'
27 require 'sbsm/drbserver'
28 require 'sbsm/index'
29 require 'util/config'
30 require 'fileutils'
31 require 'yaml'
32 require 'yus/session'
33 require 'model/migel/group'
34 require 'model/analysis/group'
35 
36 class OddbPrevalence
37 	include ODDB::Failsafe
38 	include ODBA::Persistable
39   RESULT_SIZE_LIMIT = 250
40 	ODBA_EXCLUDE_VARS = [
41 		"@atc_chooser", "@bean_counter", "@sorted_fachinfos", "@sorted_feedbacks",
42     "@sorted_minifis",
43 	]
44 	ODBA_SERIALIZABLE = [ '@currency_rates', '@rss_updates' ]
45 	attr_reader :address_suggestions, :atc_chooser, :atc_classes, :analysis_groups,
46 		:companies, :doctors, :fachinfos, :galenic_groups, :migel_groups,
47 		:hospitals, :invoices, :last_medication_update, :last_update,
48     :minifis, :notification_logger, :orphaned_fachinfos,
49     :orphaned_patinfos, :patinfos, :patinfos_deprived_sequences,
50     :registrations, :slates, :users, :narcotics, :accepted_orphans,
51     :commercial_forms, :rss_updates, :feedbacks, :indices_therapeutici,
52     :generic_groups
53 	def initialize
54 		init
55 		@last_medication_update ||= Time.now()
56 	end
57 	def init
58 		create_unknown_galenic_group()
59 		@accepted_orphans ||= {}
60 		@address_suggestions ||= {}
61 		@analysis_groups ||= {}
62 		@atc_classes ||= {}
63 		@commercial_forms ||= {}
64 		@companies ||= {}
65 		@currency_rates ||= {}
66 		@cyp450s ||= {}
67 		@doctors ||= {}
68 		@fachinfos ||= {}
69     @feedbacks ||= {}
70 		@galenic_forms ||= []
71 		@galenic_groups ||= []
72 		@generic_groups ||= {}
73 		@hospitals ||= {}
74 		@indications ||= {}
75     @indices_therapeutici ||= {}
76 		@invoices ||= {}
77 		@log_groups ||= {}
78 		@migel_groups ||= {}
79     @minifis ||= {}
80 		@narcotics ||= {}
81 		@notification_logger ||= ODDB::NotificationLogger.new
82 		@orphaned_fachinfos ||= {}
83 		@orphaned_patinfos ||= {}
84 		@patinfos ||= {}
85 		@patinfos_deprived_sequences ||= []
86 		@registrations ||= {}
87     @rss_updates ||= {}
88 		@slates ||= {}
89 		@sponsors ||= {}
90 		@substances ||= {}
91 		#recount()
92 		rebuild_atc_chooser()
93 	end
94 	# prevalence-methods ################################
95 	def create(pointer)
96 		@last_update = Time.now()
97 		failsafe {
98 			if(item = pointer.issue_create(self))
99 				updated(item)
100 				item
101 			end
102 		}
103 	end
104 	def delete(pointer)
105 		@last_update = Time.now()
106 		failsafe(ODDB::Persistence::UninitializedPathError) {
107 			if(item = pointer.resolve(self))
108 				updated(item)
109 			end
110 			pointer.issue_delete(self)
111 		}
112 	end
113 	def update(pointer, values, origin=nil)
114 		#puts [__FILE__,__LINE__,"update(#{pointer}, #{values})"].join(':')
115 		@last_update = Time.now()
116 		item = nil
117 		failsafe(ODDB::Persistence::UninitializedPathError, nil) {
118 			item = pointer.issue_update(self, values, origin)
119 			updated(item) unless(item.nil?)
120 		}
121 		item
122 	end
123 	def clean_odba_stubs
124 		_clean_odba_stubs_hash(@substances)
125 		@substances.each_value { |sub| _clean_odba_stubs_array(sub.sequences) }
126 		_clean_odba_stubs_hash(@atc_classes)
127 		@atc_classes.each_value { |atc| _clean_odba_stubs_array(atc.sequences) }
128 		_clean_odba_stubs_hash(@registrations)
129 		@registrations.each_value { |reg|
130 			_clean_odba_stubs_hash(reg.sequences)
131 			reg.sequences.each_value { |seq|
132 				_clean_odba_stubs_hash(seq.packages)
133 				_clean_odba_stubs_array(seq.active_agents)
134 			}
135 		}
136 	end
137 	def _clean_odba_stubs_hash(hash)
138 		if(hash.values.any? { |val| val.odba_instance.nil? })
139 			hash.delete_if { |key, val| val.odba_instance.nil? }
140 			hash.odba_store
141 		end
142 	end
143 	def _clean_odba_stubs_array(array)
144 		if(array.any? { |val| val.odba_instance.nil? })
145 			array.delete_if { |val| val.odba_instance.nil? }
146 			array.odba_store
147 		end
148 	end
149 	#####################################################
150 	def admin(oid)
151 		@users[oid.to_i]
152 	end
153 	def admin_subsystem
154 		ODBA.cache.fetch_named('admin', self) {
155 			ODDB::Admin::Subsystem.new
156 		}
157 	end
158   def active_fachinfos
159     active = {}
160     @registrations.each_value { |reg|
161       if(reg.active? && reg.fachinfo)
162         active.store(reg.pointer, 1)
163       end
164     }
165     active
166   end
167 	def active_pdf_patinfos
168 		active = {}
169 		each_sequence { |seq|
170 			if(str = seq.active_patinfo)
171 				active.store(str, 1)
172 			end
173 		}
174 		active
175 	end
176 	def address_suggestion(oid)
177 		@address_suggestions[oid.to_i]
178 	end
179 	def analysis_group(grpcd)
180 		@analysis_groups[grpcd]
181 	end
182 	def analysis_positions
183 		@analysis_groups.values.inject([]) { |memo, group| 
184 			memo.concat(group.positions.values)
185 		}
186 	end
187 	def atcless_sequences
188 		ODBA.cache.retrieve_from_index('atcless', 'true')
189 	end
190 	def atc_class(code)
191 		@atc_classes[code]
192 	end
193 	def atc_ddd_count
194 		@atc_ddd_count ||= count_atc_ddd()
195 	end
196 	def clean_invoices
197 		@invoices.delete_if { |oid, invoice| invoice.odba_instance.nil? }
198 		deletables = @invoices.values.select { |invoice|
199 			invoice.deletable?
200 		}
201 		unless(deletables.empty?)
202 			deletables.each { |invoice|
203 				delete(invoice.pointer)
204 			}
205 			@invoices.odba_isolated_store
206 		end
207 	end
208   def commercial_form(oid)
209     @commercial_forms[oid.to_i]
210   end
211   def commercial_form_by_name(name)
212     ODDB::CommercialForm.find_by_name(name)
213   end
214 	def company(oid)
215 		@companies[oid.to_i]
216 	end
217   def company_by_name(name, ngram_cutoff=nil)
218     _company_by_name(name, ngram_cutoff) \
219       || _company_by_name(name, ngram_cutoff, /\s*(ag|gmbh|sa)\b/i)
220   end
221   def _company_by_name(name, ngram_cutoff=nil, filter=nil)
222     namedown = name.to_s.downcase
223     if filter
224       namedown.gsub! filter, ''
225     end
226     @companies.each_value { |company|
227       name = company.name.to_s.downcase
228       if filter
229         name.gsub! filter, ''
230       end
231       if name == namedown \
232         || (ngram_cutoff \
233             && ODDB::Util::NGramSimilarity.compare(name, namedown) > ngram_cutoff)
234         return company
235       end
236     }
237     nil
238   end
239 	def company_count
240 		@company_count ||= @companies.size
241 	end
242 	def config(*args)
243 		if(@config.nil?)
244 			@config = ODDB::Config.new
245 			@config.pointer = ODDB::Persistence::Pointer.new(:config)
246 			self.odba_store
247 		end
248 		hook = @config
249 		args.each { |arg|
250 			conf = hook.send(arg)
251 			if(conf.nil?)
252 				conf = hook.send("create_#{arg}")
253 				conf.pointer = hook.pointer + arg
254 				hook.odba_store
255 			end
256 			hook = conf
257 		}
258 		hook
259 	end
260 	def count_atc_ddd
261 		@atc_classes.values.inject(0) { |inj, atc|
262 			inj += 1 if(atc.has_ddd?)
263 			inj
264 		}
265 	end
266 	def count_limitation_texts
267 		@registrations.values.inject(0) { |inj,reg|			
268 			inj + reg.limitation_text_count
269 		}
270 	end
271 	def count_packages
272 		@registrations.values.inject(0) { |inj, reg|
273 			inj + reg.active_package_count
274 		}
275 	end
276 	def count_patinfos
277 		@patinfos.size + active_pdf_patinfos.size
278 	end
279 	def count_recent_registrations
280 		if((grp = log_group(:swissmedic) || log_group(:swissmedic_journal)) \
281 			 && (log = grp.latest))
282 			log.change_flags.select { |ptr, flags|
283 				flags.include?(:new)
284 			}.size
285 		else 
286 			0
287 		end
288 	end
289 	def count_vaccines
290 		@registrations.values.inject(0) { |inj, reg|
291 			if(reg.vaccine)
292 				inj += reg.active_package_count
293 			end
294 			inj
295 		}
296 	end
297 	def cyp450(id)
298 		@cyp450s[id]
299 	end
300 	def cyp450s
301 		@cyp450s.values
302 	end
303 	def create_analysis_group(groupcd)
304 		group = ODDB::Analysis::Group.new(groupcd)
305 		@analysis_groups.store(groupcd, group)
306 	end
307 	def create_atc_class(atc_class)
308 		atc = ODDB::AtcClass.new(atc_class)
309 		@atc_chooser.add_offspring(ODDB::AtcNode.new(atc))
310 		@atc_classes.store(atc_class, atc)
311 	end
312 	def create_commercial_form
313 		form = ODDB::CommercialForm.new
314 		@commercial_forms.store(form.oid, form)
315 	end
316 	def create_company
317 		company = ODDB::Company.new
318 		@companies.store(company.oid, company)
319 	end
320 	def create_doctor
321 		doctor = ODDB::Doctor.new
322 		@doctors ||= {}
323 		@doctors.store(doctor.oid, doctor)
324 	end
325 	def create_hospital(ean13)
326 		hospital = ODDB::Hospital.new(ean13)
327 		@hospitals.store(ean13, hospital)
328 	end
329 	def create_cyp450(cyp_id)
330 		cyp450 = ODDB::CyP450.new(cyp_id)
331 		@cyp450s.store(cyp_id, cyp450)
332 	end
333 	def create_fachinfo
334 		fachinfo = ODDB::Fachinfo.new
335 		@fachinfos.store(fachinfo.oid, fachinfo)
336 	end
337   def create_feedback
338     feedback = ODDB::Feedback.new
339     @feedbacks.store(feedback.oid, feedback) 
340   end
341 	def create_galenic_group
342 		galenic_group = ODDB::GalenicGroup.new
343 		@galenic_groups.store(galenic_group.oid, galenic_group)
344 	end
345 	def create_generic_group(package_pointer)
346 		@generic_groups.store(package_pointer, ODDB::GenericGroup.new)
347 	end
348   def create_index_therapeuticus(code)
349     code = code.to_s
350     it = ODDB::IndexTherapeuticus.new(code)
351     @indices_therapeutici.store(code, it)
352   end
353 	def create_indication
354 		indication = ODDB::Indication.new
355 		@indications.store(indication.oid, indication)
356 	end
357 	def create_invoice
358 		invoice = ODDB::Invoice.new
359 		@invoices.store(invoice.oid, invoice)
360 	end
361 	def create_address_suggestion
362 		address = ODDB::AddressSuggestion.new
363 		@address_suggestions.store(address.oid, address) 
364 	end
365 	def create_log_group(key)
366 		@log_groups[key] ||= ODDB::LogGroup.new(key)
367 	end
368   def create_minifi
369     minifi = ODDB::MiniFi.new
370     @minifis.store(minifi.oid, minifi)
371   end
372 	def create_narcotic
373 		narc = ODDB::Narcotic.new
374 		@narcotics.store(narc.oid, narc)
375 	end
376 	def create_orphaned_fachinfo
377 		@orphaned_fachinfos ||= {}
378 		orphan = ODDB::OrphanedTextInfo.new
379 	  @orphaned_fachinfos.store(orphan.oid, orphan)
380 	end
381 	def create_orphaned_patinfo
382 		orphan = ODDB::OrphanedTextInfo.new
383 	  @orphaned_patinfos.store(orphan.oid, orphan)
384 	end
385 	def create_patinfo
386 		patinfo = ODDB::Patinfo.new
387 		@patinfos.store(patinfo.oid, patinfo)
388 	end
389 	def create_poweruser
390 		user = ODDB::PowerUser.new
391 		@users.store(user.oid, user)
392 	end
393 	def create_registration(iksnr)
394 		unless @registrations.include?(iksnr)
395 			reg = ODDB::Registration.new(iksnr)
396 			@registrations.store(iksnr, reg)
397 			reg
398 		end
399 	end
400 	def create_slate(name)
401 		slate = ODDB::Slate.new(name)
402 		@slates.store(name, slate)
403 	end
404 	def create_sponsor(flavor)
405 		sponsor = ODDB::Sponsor.new
406 		@sponsors.store(flavor, sponsor)
407 	end
408 	def create_substance(key=nil)
409 		if(!key.nil? && (subs = substance(key)))
410 			subs
411 		else
412 			subs = ODDB::Substance.new
413 			unless(key.nil?)
414 				values = {
415 					'lt'	=>	key,
416 				}
417 				diff = subs.diff(values, self)
418 				subs.update_values(diff)
419 			end
420 			@substances.store(subs.oid, subs)
421 		end
422 	end
423 	def create_user
424 		@users ||= {}
425 		user = ODDB::CompanyUser.new
426 		@users.store(user.oid, user)
427 	end
428 	def currencies
429 		@currency_rates.keys.sort
430 	end
431 	def create_migel_group(groupcd)
432 		migel = ODDB::Migel::Group.new(groupcd)
433 		@migel_groups.store(groupcd, migel)
434 	end
435 	def analysis_count
436 		@analysis_count ||= analysis_positions.size
437 	end
438 	def delete_address_suggestion(oid)
439 		if(sug = @address_suggestions.delete(oid))
440 			@address_suggestions.odba_isolated_store
441 			sug
442 		end
443 	end
444 	def delete_atc_class(atccode)
445 		atc = @atc_classes[atccode]
446 		@atc_chooser.delete(atccode)
447 		if(@atc_classes.delete(atccode))
448 			@atc_classes.odba_isolated_store
449 		end
450 		atc
451 	end
452 	def delete_cyp450(cyp_id)
453 		if(cyp = @cyp450s.delete(cyp_id))
454 			@cyp450s.odba_isolated_store
455 			cyp
456 		end
457 	end
458 	def delete_commercial_form(oid)
459 		if(form = @commercial_forms.delete(oid))
460 			@commercial_forms.odba_isolated_store
461 			form
462 		end
463 	end
464 	def delete_company(oid)
465 		if(comp = @companies.delete(oid))
466 			@companies.odba_isolated_store
467 			comp
468 		end
469 	end
470 	def delete_doctor(oid)
471 		if(doc = @doctors.delete(oid.to_i))
472 			@doctors.odba_isolated_store
473 			doc
474 		end
475 	end
476 	def delete_fachinfo(oid)
477 		if(fi = @fachinfos.delete(oid))
478 			@fachinfos.odba_isolated_store
479 			fi
480 		end
481 	end
482 	def delete_galenic_group(oid)
483 		group = galenic_group(oid)
484 		unless (group.nil? || group.empty?)
485 			raise 'e_nonempty_galenic_group'
486 		end
487 		if(grp = @galenic_groups.delete(oid.to_i))
488 			@galenic_groups.odba_isolated_store
489 			grp
490 		end
491 	end
492   def delete_index_therapeuticus(code)
493     code = code.to_s
494     if(it = @indices_therapeutici.delete(code))
495       @indices_therapeutici.odba_isolated_store
496       it
497     end
498   end
499 	def delete_indication(oid)
500 		if(ind = @indications.delete(oid))
501 			@indications.odba_isolated_store
502 			ind
503 		end
504 	end
505 	def delete_invoice(oid)
506 		if(inv = @invoices.delete(oid))
507 			@invoices.odba_isolated_store
508 			inv
509 		end
510 	end
511 	def delete_migel_group(code)
512 		if(grp = @migel_groups[code])
513 			@migel_groups.odba_isolated_store
514 			grp
515 		end
516 	end
517   def delete_minifi(oid)
518     if(minifi = @minifis.delete(oid.to_i))
519       @minifis.odba_isolated_store
520       minifi
521     end
522   end
523 	def delete_orphaned_fachinfo(oid)
524 		if(fi = @orphaned_fachinfos.delete(oid.to_i))
525 			@orphaned_fachinfos.odba_isolated_store
526 			fi
527 		end
528 	end
529 	def delete_orphaned_patinfo(oid)
530 		if(pi = @orphaned_patinfos.delete(oid.to_i))
531 			@orphaned_patinfos.odba_isolated_store
532 			pi
533 		end
534 	end
535 	def delete_patinfo(oid)
536 		if(fi = @patinfos.delete(oid))
537 			@patinfos.odba_isolated_store
538 			fi
539 		end
540 	end
541 	def delete_registration(iksnr)
542 		if(reg = @registrations.delete(iksnr))
543 			@registrations.odba_isolated_store
544 			reg
545 		end
546 	end
547 	def delete_substance(key)
548 		substance = nil
549 		if(key.to_i.to_s == key.to_s)
550 			substance = @substances.delete(key.to_i)
551 		else
552 			substance = @substances.delete(key.to_s.downcase)
553 		end
554 		if(substance)
555 			@substances.odba_isolated_store
556 			substance
557 		end
558 	end
559 	def doctor(oid)
560 		@doctors[oid.to_i]
561 	end
562 	def hospital(ean13)
563 		@hospitals[ean13]
564 	end
565 	def hospital_count
566 		@hospitals.size
567 	end
568 	def doctor_count
569 		@doctor_count ||= @doctors.size
570 	end
571 	def doctor_by_origin(origin_db, origin_id)
572 		# values.each instead of each_value for testing
573 		@doctors.values.each { |doctor|
574 			if(doctor.record_match?(origin_db, origin_id))
575 				return doctor
576 			end
577 		}
578 		nil
579 	end
580 	def each_atc_class(&block)
581 		@atc_classes.each_value(&block)
582 	end
583 	def each_galenic_form(&block)
584 		@galenic_groups.each_value { |galgroup|
585 			galgroup.each_galenic_form(&block)
586 		}
587 	end
588   def each_migel_product(&block)
589     @migel_groups.each_value { |group| 
590       group.subgroups.each_value { |subgr|
591         subgr.products.each_value(&block)
592       }
593     }
594   end
595 	def each_package(&block)
596 		@registrations.each_value { |reg|
597 			reg.each_package(&block)
598 		}
599 	end
600 	def each_sequence(&block)
601 		@registrations.each_value { |reg|
602 			reg.each_sequence(&block)
603 		}
604 	end
605 	def execute_command(command)
606 		command.execute(self)
607 	end
608 	def fachinfo(oid)
609 		@fachinfos[oid.to_i]
610 	end
611 	def fachinfo_count
612 		@fachinfos.size
613 	end
614 	def fachinfos_by_name(name, lang)
615 		if(lang.to_s != "fr") 
616 			lang = "de"
617 		end
618 		ODBA.cache.retrieve_from_index("fachinfo_name_#{lang}", 
619 			name)
620 	end
621   def feedback(id)
622     @feedbacks[id.to_i]
623   end
624 	def galenic_form(name)
625 		@galenic_groups.values.collect { |galenic_group|
626 			galenic_group.get_galenic_form(name)
627 		}.compact.first
628 	end
629 	def galenic_group(oid)
630 		@galenic_groups[oid.to_i]
631 	end
632 	def generic_group(package_pointer)
633 		@generic_groups[package_pointer]
634 	end
635 	def get_currency_rate(symbol)
636     ODDB::Currency.rate('CHF', symbol)
637 	end
638   def index_therapeuticus(code)
639     @indices_therapeutici[code.to_s]
640   end
641 	def indication(oid)
642 		@indications[oid.to_i]
643 	end
644 	def indication_by_text(text)
645 		@indications.values.select { |indication|
646 			indication.has_description?(text)
647 		}.first
648 	end
649 	def indications
650 		@indications.values
651 	end
652 	def invoice(oid)
653 		@invoices ||= {}
654 		@invoices[oid.to_i]
655 	end
656 	def limitation_text_count
657 		@limitation_text_count ||= count_limitation_texts()
658 	end
659 	def log_group(key)
660 		@log_groups[key]
661 	end
662 	def migel_count
663 		@migel_count ||= migel_products.size	
664 	end
665 	def migel_group(groupcd)
666 		@migel_groups[groupcd]
667 	end
668 	def migel_product(code)
669 		parts = code.split('.', 3)
670 		migel_group(parts[0]).subgroup(parts[1]).product(parts[2])
671 	rescue NoMethodError
672 		# invalid migel_code
673 		nil
674 	end
675 	def migel_products
676 		products = []
677 		@migel_groups.each_value { |group| 
678 			group.subgroups.each_value { |subgr|
679 				products.concat(subgr.products.values)
680 			}
681 		}
682 		products
683 	end
684   def minifi(oid)
685     @minifis[oid.to_i]
686   end
687 	def narcotic(odba_id)
688 		@narcotics[odba_id.to_i]
689 	end
690 	def narcotic_by_casrn(casrn)
691 		unless(casrn.nil?)
692       @narcotics.values.find do |narc| narc.casrn == casrn end
693     end
694 	end
695 	def narcotic_by_smcd(smcd)
696 		unless(smcd.nil?)
697       @narcotics.values.find do |narc| narc.swissmedic_codes.include?(smcd) end
698 		end
699 	end
700 	def narcotics_count
701 		@narcotics.size
702 	end
703 	def orphaned_fachinfo(oid)
704 		@orphaned_fachinfos[oid.to_i]
705 	end
706 	def orphaned_patinfo(oid)
707 		@orphaned_patinfos[oid.to_i]
708 	end
709   def package(pcode)
710     ODDB::Package.find_by_pharmacode(pcode.to_s.gsub(/^0+/u, ''))
711   end
712   def package_by_ikskey(ikskey)
713     ikskey = ikskey.to_s
714     iksnr = "%05i" % ikskey[-8..-4].to_i
715     ikscd = ikskey[-3..-1]
716     if reg = registration(iksnr)
717       reg.package ikscd
718     end
719   end
720 	def package_count
721 		@package_count ||= count_packages()
722 	end
723   def packages
724     @registrations.inject([]) { |pacs, (iksnr,reg)| 
725       pacs.concat(reg.packages)
726     }
727   end
728 	def patinfo(oid)
729 		@patinfos[oid.to_i]
730 	end
731 	def patinfo_count
732 		@patinfo_count ||= count_patinfos()
733 	end
734 	def poweruser(oid)
735 		@users[oid.to_i]
736 	end
737 	def rebuild_atc_chooser
738 		chooser = ODDB::AtcNode.new(nil)
739 		@atc_classes.sort.each { |key, atc| 
740 			chooser.add_offspring(ODDB::AtcNode.new(atc))
741 		}
742 		@atc_chooser = chooser
743 	end
744 	def recent_registration_count
745 		@recent_registration_count ||= count_recent_registrations()
746 	end
747 	def recount
748     again = true
749 		if(@bean_counter.is_a?(Thread) && @bean_counter.status)
750 			return again = true
751 		end
752 		@bean_counter = Thread.new {
753       while(again)
754         again = false
755         @analysis_count = analysis_positions.size
756         @atc_ddd_count = count_atc_ddd()
757         @doctor_count = @doctors.size
758         @company_count = @companies.size
759         @substance_count = @substances.size
760         @limitation_text_count = count_limitation_texts()
761         @migel_count = migel_products.size
762         @package_count = count_packages()
763         @patinfo_count = count_patinfos()
764         @recent_registration_count = count_recent_registrations()
765         @vaccine_count = count_vaccines()
766         self.odba_isolated_store
767       end
768 		}
769 	end
770 	def registration(registration_id)
771 		@registrations[registration_id]
772 	end
773 	def resolve(pointer)
774 		pointer.resolve(self)
775 	end
776 	def refactor_addresses
777 		# 3 Iterationen 
778 		puts "refactoring doctors"
779 		$stdout.flush
780 	  @doctors.each_value { |doc| 
781 			doc.refactor_addresses 
782 			doc.odba_store
783 		}
784 		puts "refactoring hospitals"
785 		$stdout.flush
786 	  @hospitals.each_value { |spi| 
787 			spi.refactor_addresses 
788 			spi.odba_store
789 		}
790 		puts "refactoring companies"
791 		$stdout.flush
792 	  @companies.each_value { |comp| 
793 			comp.refactor_addresses 
794 			comp.odba_store
795 		}
796 		puts "finished refactoring addresses"
797 		$stdout.flush
798 	end
799 	def search_analysis(key, lang)
800 		if(lang == 'en')
801 			lang = 'de'
802 		end
803 		ODBA.cache.retrieve_from_index("analysis_index_#{lang}", key)
804 	end
805 	def search_analysis_alphabetical(query, lang)
806 		if(lang == 'en')
807 			lang = 'de'
808 		end
809 		index_name = "analysis_alphabetical_index_#{lang}"
810 		ODBA.cache.retrieve_from_index(index_name, query)
811 	end
812   @@iks_or_ean = /(?:\d{4})?(\d{5})(?:\d{4})?/u
813 	def search_oddb(query, lang)
814 		# current search_order:
815 		# 1. atcless
816 		# 2. iksnr or ean13
817 		# 3. atc-code
818 		# 4. exact word in sequence name
819 		# 5. company-name
820 		# 6. substance
821 		# 7. indication
822 		# 8. sequence
823 		result = ODDB::SearchResult.new
824 		result.exact = true
825 		result.search_query = query
826 		# atcless
827 		if(query == 'atcless')
828 			atc = ODDB::AtcClass.new('n.n.')
829 			atc.sequences = atcless_sequences
830 			atc.instance_eval {
831 				alias :active_packages :packages
832 			}
833 			result.atc_classes = [atc]
834 			result.search_type = :atcless
835 			return result
836     end
837 		# iksnr or ean13
838 		if(match = @@iks_or_ean.match(query))
839 			iksnr = match[1]
840 			if(reg = registration(iksnr))
841 				atc = ODDB::AtcClass.new('n.n.')
842 				atc.sequences = reg.sequences.values
843 				result.atc_classes = [atc]
844 				result.search_type = :iksnr
845 				return result
846 			end
847     end
848 		# pharmacode
849     if(match = /^\d{6,}$/u.match(query))
850       if(pac = package(query))
851 				atc = ODDB::AtcClass.new('n.n.')
852         seq = ODDB::Sequence.new(pac.sequence.seqnr)
853         seq.registration = pac.registration
854         seq.packages.store pac.ikscd, pac
855 				atc.sequences = [seq]
856 				result.atc_classes = [atc]
857 				result.search_type = :pharmacode
858 				return result
859       end
860 		end
861 		key = query.to_s.downcase
862 		# atc-code
863 		atcs = search_by_atc(key)
864 		result.search_type = :atc
865     result.error_limit = RESULT_SIZE_LIMIT
866 		# exact word in sequence name
867 		if(atcs.empty?)
868 			atcs = search_by_sequence(key, result)
869 			result.search_type = :sequence
870 		end
871 		# company-name
872 		if(atcs.empty?)
873 			atcs = search_by_company(key)
874 			result.search_type = :company
875 		end
876 		# substance
877 		if(atcs.empty?)
878 			atcs = search_by_substance(key)
879 			result.search_type = :substance
880 		end
881 		# indication
882 		if(atcs.empty?)
883 			atcs = search_by_indication(key, lang, result)
884 			result.search_type = :indication
885 		end
886 		# sequence
887 		if(atcs.empty?)
888 			atcs = search_by_sequence(key)
889 			result.search_type = :sequence
890 		end
891 		result.atc_classes = atcs
892 		# interaction
893 		if(atcs.empty?)
894 			result = search_by_interaction(key, lang)
895 		end
896 		# unwanted effects
897 		if(result.atc_classes.empty?)
898 			result = search_by_unwanted_effect(key, lang)
899 		end
900 		result
901 	end
902 	def search_by_atc(key)
903 		ODBA.cache.retrieve_from_index('atc_index', key.dup)
904 	end
905 	def search_by_company(key)
906 		result = ODDB::SearchResult.new
907     result.error_limit = RESULT_SIZE_LIMIT
908 		atcs = ODBA.cache.retrieve_from_index('atc_index_company', key.dup, result)
909 		filtered = atcs.collect { |atc|
910 			atc.company_filter_search(key.dup)
911 		}
912 		filtered.flatten.compact.uniq
913 	end
914 	def search_by_indication(key, lang, result)
915 		if(lang.to_s != "fr") 
916 			lang = "de"
917 		end
918 		atcs = ODBA.cache.\
919 			retrieve_from_index("fachinfo_index_#{lang}", key.dup, result)
920 		atcs += ODBA.cache.\
921 			retrieve_from_index("indication_index_atc_#{lang}",
922 			key.dup, result)
923 		atcs.uniq
924 	end
925 	def search_by_sequence(key, result=nil)
926 		ODBA.cache.retrieve_from_index('sequence_index_atc', key.dup, result)
927 	end
928 	def search_by_interaction(key, lang)
929 		result = ODDB::SearchResult.new
930     result.error_limit = RESULT_SIZE_LIMIT
931 		if(lang.to_s != "fr") 
932 			lang = "de"
933 		end
934 		sequences = ODBA.cache.retrieve_from_index("interactions_index_#{lang}", 
935                                                key, result)
936     key = key.downcase
937     sequences.reject! { |seq| 
938       ODDB.search_terms(seq.search_terms, :downcase => true).include?(key) \
939         || seq.substances.any? { |sub|
940         sub.search_keys.any? { |skey| skey.downcase.include?(key) }
941       }
942     }
943 		_search_exact_classified_result(sequences, :interaction, result)
944 	end
945 	def search_by_substance(key)
946 		ODBA.cache.retrieve_from_index('substance_index_atc', key.dup)
947 	end
948 	def search_by_unwanted_effect(key, lang)
949 		result = ODDB::SearchResult.new
950 		if(lang.to_s != "fr") 
951 			lang = "de"
952 		end
953 		sequences = ODBA.cache.retrieve_from_index("unwanted_effects_index_#{lang}", 
954                                                key, result)
955 		_search_exact_classified_result(sequences, :unwanted_effect, result)
956 	end
957 	def search_doctors(key)
958 		ODBA.cache.retrieve_from_index("doctor_index", key)
959 	end
960 	def search_companies(key)
961 		ODBA.cache.retrieve_from_index("company_index", key)
962 	end
963 	def search_exact_company(query)
964 		result = ODDB::SearchResult.new
965 		result.search_type = :company
966 		result.atc_classes = search_by_company(query)
967 		result
968 	end
969 	def search_exact_indication(query, lang)
970 		result = ODDB::SearchResult.new
971 		result.exact = true
972 		result.search_type = :indication
973 		result.atc_classes = search_by_indication(query, lang, result)
974 		result
975 	end
976 	def search_migel_alphabetical(query, lang)
977 		if(lang.to_s != "fr") 
978 			lang = "de"
979 		end
980 		index_name = "migel_index_#{lang}"
981 		ODBA.cache.retrieve_from_index(index_name, query)
982 	end
983 	def search_migel_products(query, lang)
984 		if(lang.to_s != "fr") 
985 			lang = "de"
986 		end
987 		index_name = "migel_fulltext_index_#{lang}"
988 		ODBA.cache.retrieve_from_index(index_name, query)
989 	end
990 	def search_narcotics(query, lang)
991 		if(lang.to_s != "fr") 
992 			lang = "de"
993 		end
994 		index_name = "narcotics_#{lang}"
995 		ODBA.cache.retrieve_from_index(index_name, query)
996 	end
997 	def search_patinfos(query)
998 		ODBA.cache.retrieve_from_index('sequence_patinfos', query)
999 	end
1000 	def search_vaccines(query)
1001 		ODBA.cache.retrieve_from_index('sequence_vaccine', query)
1002 	end
1003 	def search_exact_sequence(query)
1004 		sequences = search_sequences(query)
1005 		_search_exact_classified_result(sequences, :sequence)
1006 	end
1007 	def search_exact_substance(query)
1008 		sequences = ODBA.cache.\
1009 			retrieve_from_index('substance_index_sequence', query)
1010 		_search_exact_classified_result(sequences, :substance)
1011 	end
1012 	def _search_exact_classified_result(sequences, type=:unknown, result=nil)
1013 		atc_classes = {}
1014 		sequences.each { |seq|
1015 			code = (atc = seq.atc_class) ? atc.code : 'n.n'
1016 			new_atc = atc_classes.fetch(code) { 
1017 				atc_class = ODDB::AtcClass.new(code)
1018 				atc_class.descriptions = atc.descriptions unless(atc.nil?)
1019 				atc_classes.store(code, atc_class)
1020 			}
1021 			new_atc.sequences.push(seq)
1022 		}
1023 		result ||= ODDB::SearchResult.new
1024 		result.search_type = type
1025 		result.atc_classes = atc_classes.values
1026 		result
1027 	end
1028 	def search_hospitals(key)
1029 		ODBA.cache.retrieve_from_index("hospital_index", key)
1030 	end
1031 	def search_indications(query)
1032 		ODBA.cache.retrieve_from_index("indication_index", query)
1033 	end
1034 	def search_interactions(query)
1035 		result = ODBA.cache.retrieve_from_index("sequence_index_substance", query)
1036 		if(subs = substance(query, false))
1037 			result.unshift(subs)
1038 		end
1039 		if(result.empty?)
1040 			result = soundex_substances(query)
1041 		end
1042 		result
1043 	end
1044 	def search_sequences(query, chk_all_words=true)
1045 		index = (chk_all_words) ? 'sequence_index' : 'sequence_index_exact'
1046 		ODBA.cache.retrieve_from_index(index, query)
1047 	end
1048 	def search_single_substance(key)
1049 		result = ODDB::SearchResult.new
1050 		result.exact = true
1051     key = ODDB.search_term(key)
1052 		ODBA.cache.retrieve_from_index("substance_index", key, result).find { |sub|
1053       sub.same_as? key
1054     }
1055 	end
1056 	def search_substances(query)
1057 		if(subs = substance(query))
1058 			[subs]
1059 		else
1060 			soundex_substances(query)
1061 		end
1062 	end
1063 	def sequences
1064 		@registrations.values.inject([]) { |seq, reg| 
1065 			seq.concat(reg.sequences.values)
1066 		}
1067 	end
1068 	def set_currency_rate(symbol, value)
1069 		@currency_rates.store(symbol, value)
1070 	end
1071 	def slate(name)
1072 		@slates[name]
1073 	end
1074 	def soundex_substances(name)
1075 		parts = ODDB::Text::Soundex.prepare(name).split(/\s+/u)
1076 		soundex = ODDB::Text::Soundex.soundex(parts)
1077 		key = soundex.join(' ')
1078 		ODBA.cache.retrieve_from_index("substance_soundex_index", key)
1079 	end
1080   def sorted_fachinfos
1081     @sorted_fachinfos ||= @fachinfos.values.select { |fi| 
1082       fi.revision }.sort_by { |fi| fi.revision }.reverse
1083   end
1084   def sorted_feedbacks
1085     @sorted_feedbacks ||= @feedbacks.values.sort_by { |fb| fb.time }.reverse
1086   end
1087   def sorted_minifis
1088     @sorted_minifis ||= @minifis.values.sort_by { |minifi| 
1089       [ -minifi.publication_date.year, 
1090         -minifi.publication_date.month, minifi.name] }
1091   end
1092   def sorted_patented_registrations
1093     @registrations.values.select { |reg|
1094       (pat = reg.patent) && pat.expiry_date #_protected?
1095     }.sort_by { |reg| reg.patent.expiry_date }
1096   end
1097 	def sponsor(flavor)
1098 		@sponsors[flavor.to_s]
1099 	end
1100 	def substance(key, neurotic=false)
1101 		if(key.to_i.to_s == key.to_s)
1102 			@substances[key.to_i]
1103 		elsif(substance = search_single_substance(key))
1104 			substance
1105     elsif neurotic
1106 			@substances.values.find { |subs|
1107 				subs.same_as?(key)
1108 			}
1109 		end
1110 	end
1111 	def substance_by_connection_key(connection_key)
1112 		@substances.values.select { |substance|
1113 			substance.has_connection_key?(connection_key)
1114 		}.first
1115 	end
1116 	def substance_by_smcd(smcd)
1117 		@substances.values.select { |sub|
1118 			sub.swissmedic_code == smcd
1119 		}.first
1120 	end
1121 	def substances
1122 		@substances.values
1123 	end
1124 	def substance_count
1125 		@substance_count ||= @substances.size
1126 	end
1127 	def updated(item)
1128 		case item
1129 		when ODDB::Registration, ODDB::Sequence, ODDB::Package, ODDB::AtcClass
1130 			@last_medication_update = @@today
1131 			odba_isolated_store
1132 		when ODDB::LimitationText, ODDB::AtcClass::DDD
1133 		when ODDB::Substance
1134 			@substances.each_value { |subs|
1135 				if(!subs.is_effective_form? && subs.effective_form == item)
1136 					subs.odba_isolated_store
1137 				end
1138 			}
1139     when ODDB::Fachinfo, ODDB::FachinfoDocument
1140       @sorted_fachinfos = nil
1141     when ODDB::Feedback
1142       @sorted_feedbacks = nil
1143     when ODDB::MiniFi
1144       @sorted_minifis = nil
1145 		end
1146 	end
1147 	def user(oid)
1148 		@users[oid]
1149 	end
1150 	def user_by_email(email)
1151 		@users.values.find { |user| user.unique_email == email }
1152 	end
1153 	def unique_atc_class(substance)
1154 	 atc_array = search_by_substance(substance)
1155 =begin ## this is much too unstable, completely wrong assignment is 
1156        ## probable!
1157 	 if(atc_array.size > 1)
1158 		 atc_array = atc_array.select { |atc|
1159 			 atc.substances.size == 1
1160 		 }
1161 	 end
1162 =end
1163 	 if(atc_array.size == 1)
1164 		 atc_array.first
1165 	 end
1166   end
1167 	def vaccine_count
1168 		@vaccine_count ||= count_vaccines()
1169 	end
1170 
1171 	## indices
1172 	def rebuild_indices(name=nil, &block)
1173 		ODBA.cache.indices.size
1174 		begin
1175 			start = Time.now
1176 			path = File.expand_path("../../etc/index_definitions.yaml", 
1177 				File.dirname(__FILE__))
1178 			FileUtils.mkdir_p(File.dirname(path))
1179 			file = File.open(path)
1180 			YAML.load_documents(file) { |index_definition|
1181         doit = if(name)
1182                  name.match(index_definition.index_name)
1183                elsif(block)
1184                  block.call(index_definition)
1185                else
1186                  true
1187                end
1188 				if(doit)
1189 					index_start = Time.now
1190 					begin
1191 						puts "dropping: #{index_definition.index_name}"
1192 						ODBA.cache.drop_index(index_definition.index_name)
1193 					rescue StandardError => e
1194 						puts e.message
1195 					end
1196 					puts "creating: #{index_definition.index_name}"
1197 					ODBA.cache.create_index(index_definition, ODDB)
1198 					begin 
1199 						puts "filling: #{index_definition.index_name}"
1200 						puts index_definition.init_source
1201 						source = instance_eval(index_definition.init_source)
1202 						puts "source.size: #{source.size}"
1203 						ODBA.cache.fill_index(index_definition.index_name, 
1204 							source)
1205 					rescue StandardError => e
1206 						puts e.class
1207 						puts e.message
1208 						puts e.backtrace
1209 					end
1210 					puts "finished in #{(Time.now - index_start) / 60.0} min"
1211 				end
1212 			}
1213 			puts "all Indices Created in total: #{(Time.now - start) / 3600.0} h"
1214 		rescue StandardError => e
1215 			puts "INDEX CREATION ERROR:"
1216 			puts e.message
1217 			puts e.backtrace
1218 		ensure
1219 			file.close
1220 		end
1221 	end
1222 	def generate_dictionary(language, locale)
1223 		ODBA.storage.remove_dictionary(language)
1224 		base = File.expand_path("../../ext/fulltext/data/dicts/#{language}", 
1225 			File.dirname(__FILE__))
1226 		ODBA.storage.generate_dictionary(language, locale, base)
1227 	end
1228 	def generate_dictionaries
1229 		generate_french_dictionary
1230 		generate_german_dictionary
1231 	end
1232 	def generate_french_dictionary
1233 		generate_dictionary('french', 'fr_FR@euro')
1234 	end
1235 	def generate_german_dictionary
1236 		generate_dictionary('german', 'de_DE@euro')
1237 	end
1238 	private
1239 	def create_unknown_galenic_group
1240 		unless(@galenic_groups.is_a?(Hash) && @galenic_groups.size > 0)
1241 			@galenic_groups = {}
1242 			pointer = ODDB::Persistence::Pointer.new([:galenic_group])
1243 			group = create(pointer)
1244 			raise "Default GalenicGroup has illegal Object ID (#{group.oid})" unless group.oid == 1
1245 			update(group.pointer, {'de'=>'Unbekannt'})
1246 		end
1247 	end
1248 end
1249 
1250 module ODDB
1251 	class App < SBSM::DRbServer
1252 		include Failsafe
1253 		AUTOSNAPSHOT = true
1254 		CLEANING_INTERVAL = 5*60
1255 		EXPORT_HOUR = 2
1256 		UPDATE_HOUR = 9
1257     MEMORY_LIMIT = 20480
1258 		RUN_CLEANER = true
1259 		RUN_UPDATER = true
1260 		SESSION = Session
1261 		UNKNOWN_USER = UnknownUser
1262 		UPDATE_INTERVAL = 24*60*60
1263 		VALIDATOR = Validator
1264     YUS_SERVER = DRb::DRbObject.new(nil, YUS_URI)
1265 		attr_reader :cleaner, :updater
1266 		def initialize opts={}
1267       @rss_mutex = Mutex.new
1268 			@admin_threads = ThreadGroup.new
1269       start = Time.now
1270 			@system = ODBA.cache.fetch_named('oddbapp', self){
1271 				OddbPrevalence.new
1272 			}
1273 			puts "init system"
1274 			@system.init
1275 			@system.odba_store
1276       puts "init system: #{Time.now - start}"
1277 			puts "setup drb-delegation"
1278 			super(@system)
1279       return if opts[:auxiliary]
1280 			puts "reset"
1281 			reset()
1282       puts "reset: #{Time.now - start}"
1283       log_size
1284 			puts "system initialized"
1285       puts "initialized: #{Time.now - start}"
1286 		end
1287 		# prevalence-methods ################################
1288 		def accept_orphaned(orphan, pointer, symbol, origin=nil)
1289 			command = AcceptOrphan.new(orphan, pointer,symbol, origin)
1290 			@system.execute_command(command)
1291 		end
1292 		def clean
1293 			super
1294 			@system.clean_invoices
1295 		end
1296 		def create(pointer)
1297 			@system.execute_command(CreateCommand.new(pointer))
1298 		end
1299     def create_commercial_forms
1300       @system.each_package { |pac| 
1301         if(comform = pac.comform)
1302           possibilities = [
1303             comform.strip,
1304             comform.gsub(/\([^\)]+\)/u, '').strip,
1305             comform.gsub(/[()]/u, '').strip,
1306           ].uniq.delete_if { |possibility| possibility.empty? }
1307           cform = nil
1308           possibilities.each { |possibility|
1309             if(cform = CommercialForm.find_by_name(possibility))
1310               break
1311             end
1312           }
1313           if(cform.nil?)
1314             args = { :de => possibilities.first, 
1315               :synonyms => possibilities[1..-1] }
1316             possibilities.each { |possibility|
1317               if(form = @system.galenic_form(possibility))
1318                 args = form.descriptions
1319                 args.store(:synonyms, form.synonyms)
1320                 break
1321               end
1322             }
1323             pointer = Persistence::Pointer.new(:commercial_form)
1324             cform = @system.update(pointer.creator, args)
1325           end
1326           pac.commercial_form = cform
1327           pac.odba_store
1328         end
1329       }
1330     end
1331 		def delete(pointer)
1332 			@system.execute_command(DeleteCommand.new(pointer))
1333 		end
1334     def inject_poweruser(email, pass, days)
1335       user_pointer = Persistence::Pointer.new(:poweruser)
1336       user_data = {
1337         :unique_email => email,
1338         :pass_hash    => Digest::MD5.hexdigest(pass),
1339       }
1340       invoice_pointer = Persistence::Pointer.new(:invoice)
1341       time = Time.now
1342       expiry = InvoiceItem.expiry_time(days, time)
1343       invoice_data = { :currency => State::PayPal::Checkout::CURRENCY }
1344       item_data = {
1345         :duration     => days,
1346         :expiry_time  => expiry,
1347         :total_netto  => State::Limit.price(days.to_i),
1348         :quantity     => days,
1349         :text         => 'unlimited access',
1350         :time         => time,
1351         :type         => :poweruser,
1352         :vat_rate     => VAT_RATE,
1353       }
1354       user = @system.update(user_pointer.creator, user_data, :admin)
1355       invoice = @system.update(invoice_pointer.creator, invoice_data, :admin)
1356       item_pointer = invoice.pointer + [:item]
1357       @system.update(item_pointer.creator, item_data, :admin)
1358       user.add_invoice(invoice)
1359       invoice.payment_received!
1360       invoice.odba_isolated_store
1361     end
1362 		def merge_commercial_forms(source, target)
1363 			command = MergeCommand.new(source.pointer, target.pointer)
1364 			@system.execute_command(command)
1365 		end
1366 		def merge_companies(source_pointer, target_pointer)
1367 			command = MergeCommand.new(source_pointer, target_pointer)
1368 			@system.execute_command(command)
1369 		end
1370 		def merge_galenic_forms(source, target)
1371 			command = MergeCommand.new(source.pointer, target.pointer)
1372 			@system.execute_command(command)
1373 		end
1374     def merge_indications(source, target)
1375       command = MergeCommand.new(source.pointer, target.pointer)
1376       @system.execute_command(command)
1377     end
1378 		def merge_substances(source_pointer, target_pointer)
1379 			command = MergeCommand.new(source_pointer, target_pointer)
1380 			@system.execute_command(command)
1381 		end
1382 		def replace_fachinfo(iksnr, pointer)
1383 			@system.execute_command(ReplaceFachinfoCommand.new(iksnr, pointer))
1384 		end
1385 		def update(pointer, values, origin=nil)
1386 			@system.update(pointer, values, origin)
1387 		end
1388 		#####################################################
1389 		def _admin(src, result, priority=0)
1390 			t = Thread.new {
1391 				Thread.current.abort_on_exception = false
1392 				result << failsafe {
1393 					response = instance_eval(src)
1394 					str = response.to_s
1395 					if(str.length > 200)
1396 						response.class
1397 					else
1398 						str
1399 					end
1400 				}.to_s
1401 			}
1402 			t[:source] = src
1403 			t.priority = priority
1404 			@admin_threads.add(t)
1405 			t
1406 		end
1407 		def login(email, pass)
1408       YusUser.new(YUS_SERVER.login(email, pass, YUS_DOMAIN))
1409 		end
1410 		def login_token(email, token)
1411       YusUser.new(YUS_SERVER.login_token(email, token, YUS_DOMAIN))
1412 		end
1413     def logout(session)
1414       YUS_SERVER.logout(session)
1415     rescue DRb::DRbError, RangeError
1416     end
1417     def peer_cache cache
1418       ODBA.peer cache
1419     end
1420 		def reset
1421 			@random_updater.kill if(@random_updater.is_a? Thread)
1422       if RUN_UPDATER
1423         @random_updater = run_random_updater
1424       end
1425 			@mutex.synchronize {
1426 				@sessions.clear
1427 			}
1428 		end
1429     def run_random_updater
1430       Thread.new {
1431         Thread.current.abort_on_exception = true
1432         update_hour = rand(24)
1433         update_min = rand(60)
1434         today = (update_hour > Time.now.hour) ? \
1435           @@today : @@today.next
1436         loop {
1437           next_run = Time.local(today.year, today.month, today.day,
1438             update_hour, update_min)
1439           puts "next random update will take place at #{next_run}"
1440           $stdout.flush
1441           sleep(next_run - Time.now)
1442           Updater.new(self).run_random
1443           @system.recount
1444           GC.start
1445           today = @@today.next
1446           update_hour = rand(24)
1447           update_min = rand(60)
1448         }
1449       }
1450     end
1451     def unpeer_cache cache
1452       ODBA.unpeer cache
1453     end
1454     def update_feedback_rss_feed
1455       async {
1456         begin
1457         @rss_mutex.synchronize {
1458           values = @system.sorted_feedbacks
1459           plg = Plugin.new(self)
1460           plg.update_rss_feeds('feedback.rss', values, View::Rss::Feedback)
1461         }
1462         rescue StandardError => e
1463           puts e.message
1464           puts e.backtrace
1465         end
1466       }
1467     end
1468 
1469     def ipn(notification)
1470       Util::Ipn.process notification, self
1471       nil # don't return the invoice back across drb - it's not defined in yipn
1472     end
1473     def grant_download(email, filename, price, expires=Time.now+2592000)
1474       ip = Persistence::Pointer.new(:invoice)
1475       inv = update ip.creator, :yus_name => email, :currency => 'EUR'
1476       itp = inv.pointer + :item
1477       update itp.creator, :text => filename, :price => price, :time => Time.now,
1478                           :type => :download, :expiry_time => expires,
1479                           :duration => (Time.now - expires) / 86400,
1480                           :vat_rate => 8.0
1481       inv.payment_received!
1482       inv.odba_store
1483       "http://#{SERVER_NAME}/de/gcc/download/invoice/#{inv.oid}/email/#{email}/filename/#{filename}"
1484     end
1485 
1486 		def assign_effective_forms(arg=nil)
1487       _assign_effective_forms(arg)
1488 		end
1489 		def _assign_effective_forms(arg=nil)
1490 			result = nil
1491 			last = nil
1492 			@system.substances.select { |subs| 
1493 				!subs.has_effective_form? && (arg.nil? || arg.to_s < subs.to_s)
1494 			}.sort_by { |subs| subs.name }.each { |subs|
1495 				puts "Looking for effective form of ->#{subs}<- (#{subs.sequences.size} Sequences)"
1496 				name = subs.to_s
1497 				parts = name.split(/\s/u)
1498 				suggest = if(parts.size == 1)
1499 					subs
1500 				elsif(![nil, '', 'Acidum'].include?(parts.first))
1501 					@system.search_single_substance(parts.first) \
1502 						|| @system.search_single_substance(parts.first.gsub(/i$/u, 'um'))
1503 				end
1504 				last = result
1505 				result = nil
1506 				while(result.nil?)
1507 					possibles = [
1508 						"d(elete)", 
1509 						"S(elf)", 
1510 						"n(othing)", 
1511 						"other_name",
1512 					]
1513 					if(suggest)
1514 						puts "Suggestion:                   ->#{suggest}<-"
1515 						possibles.unshift("s(uggestion)")
1516 					end
1517 					if(last)
1518 						puts "Last:                         ->#{last}<-"
1519 						possibles.unshift("l(ast)")
1520 					end
1521 					print possibles.join(", ")
1522 					print " > "
1523 					$stdout.flush
1524 					answer = $stdin.readline.strip
1525 					puts "you typed:                    ->#{answer}<-"
1526 					case answer
1527 					when ''
1528 						# do nothing
1529 					when 'l'
1530 						result = last
1531 					when 's'
1532 						result = suggest
1533 					when 'S'
1534 						result = subs
1535 					when 'd'
1536 						subs.sequences.each { |seq| 
1537 							seq.delete_active_agent(subs) 
1538 							seq.active_agents.odba_isolated_store
1539 						}
1540 						subs.odba_delete
1541 						break
1542 					when 'n'
1543 						break
1544 					when 'q'
1545 						return
1546 					when /c .+/u
1547 						puts "creating:"
1548 						pointer = Persistence::Pointer.new(:substance)
1549 						puts "pointer: #{pointer}"
1550 						args = { :lt => answer.split(/\s+/u, 2).last.strip }
1551 						argstr = args.collect { |*pair| pair.join(' => ') }.join(', ')
1552 						puts "args: #{argstr}"
1553 						result = @system.update(pointer.creator, args)
1554 						result.effective_form = result
1555 						result.odba_store
1556 						puts "result: #{result}"
1557 					else
1558 						result = @system.substance(answer)
1559 					end
1560 				end
1561 				if(result)
1562 					subs.effective_form = result
1563 					subs.odba_store
1564 				end
1565 			}
1566 			nil
1567 		end
1568 
1569     def yus_allowed?(email, action, key=nil)
1570       YUS_SERVER.autosession(YUS_DOMAIN) { |session|
1571         session.entity_allowed?(email, action, key)
1572       }
1573     end
1574     def yus_create_user(email, pass=nil)
1575       YUS_SERVER.autosession(YUS_DOMAIN) { |session|
1576         session.create_entity(email, pass)
1577       }
1578       # if there is a password, we can log in
1579       login(email, pass) if(pass)
1580     end
1581     def yus_grant(name, key, item, expires=nil)
1582       YUS_SERVER.autosession(YUS_DOMAIN) { |session|
1583         session.grant(name, key, item, expires)
1584       }
1585     end
1586     def yus_get_preference(name, key)
1587       YUS_SERVER.autosession(YUS_DOMAIN) { |session|
1588         session.get_entity_preference(name, key)
1589       }
1590     rescue Yus::YusError
1591       # user not found
1592     end
1593     def yus_get_preferences(name, keys)
1594       YUS_SERVER.autosession(YUS_DOMAIN) { |session|
1595         session.get_entity_preferences(name, keys)
1596       }
1597     rescue Yus::YusError
1598       {} # return an empty hash
1599     end
1600     def yus_model(name)
1601       if(odba_id = yus_get_preference(name, 'association'))
1602         ODBA.cache.fetch(odba_id, nil)
1603       end
1604     rescue Yus::YusError, ODBA::OdbaError
1605       # association not found
1606     end
1607     def yus_reset_password(name, token, password)
1608       YUS_SERVER.autosession(YUS_DOMAIN) { |session|
1609         session.reset_entity_password(name, token, password)
1610       }
1611     end
1612     def yus_set_preference(name, key, value, domain=YUS_DOMAIN)
1613       YUS_SERVER.autosession(YUS_DOMAIN) { |session|
1614         session.set_entity_preference(name, key, value, domain)
1615       }
1616     end
1617 
1618 		def multilinguify_analysis
1619 			@system.analysis_positions.each { |pos|
1620 				if(descr = pos.description)
1621 					pos.instance_variable_set('@description', nil)
1622 					@system.update(pos.pointer, {:de => descr})
1623 				end
1624 				if(fn = pos.footnote)
1625 					pos.instance_variable_set('@footnote', nil)
1626 					ptr = pos.pointer + :footnote
1627 					@system.update(ptr.creator, {:de => fn})
1628 				end
1629 				if(lt = pos.list_title)
1630 					pos.instance_variable_set('@list_title', nil)
1631 					ptr = pos.pointer + :list_title
1632 					@system.update(ptr.creator, {:de => lt})
1633 				end
1634 				if(tn = pos.taxnote)
1635 					pos.instance_variable_set('@taxnote', nil)
1636 					ptr = pos.pointer + :taxnote
1637 					@system.update(ptr.creator, {:de => tn})
1638 				end
1639 				if(perm = pos.permissions)
1640 					pos.instance_variable_set('@permissions', nil)
1641 					ptr = pos.pointer + :permissions
1642 					@system.update(ptr.creator, {:de => perm})
1643 				end
1644 				if(lim = pos.instance_variable_get('@limitation'))
1645 					pos.instance_variable_set('@limitation', nil)
1646 					ptr = pos.pointer + :limitation_text
1647 					@system.update(ptr.creator, {:de => lim})
1648 				end
1649 				pos.odba_store
1650 			}
1651 		end
1652     def migrate_feedbacks
1653       @system.each_package { |pac|
1654         _migrate_feedbacks(pac)
1655       }
1656       @system.each_migel_product { |prd|
1657         _migrate_feedbacks(prd)
1658       }
1659       @system.feedbacks.odba_store
1660       @system.odba_store
1661       update_feedback_rss_feed
1662     end
1663     def _migrate_feedbacks(item)
1664       item = item.odba_instance
1665       fbs = item.instance_variable_get('@feedbacks').odba_instance
1666       case fbs
1667       when Array
1668         # already migrated, ignore
1669       when Hash
1670         new = fbs.values.select { |fb| 
1671           fb.is_a?(Feedback) 
1672         }.sort_by { |fb| fb.time }.reverse
1673         fbs.odba_delete
1674         new.odba_store
1675         item.instance_variable_set('@feedbacks', new)
1676         item.odba_store
1677         new.each { |fb|
1678           id = fb.odba_id
1679           fb.instance_variable_set('@oid', id)
1680           ptr = Persistence::Pointer.new([:feedback, id])
1681           fb.instance_variable_set('@pointer', ptr)
1682           @system.feedbacks.store(id, fb)
1683           fb.instance_variable_set('@item', item)
1684           fb.odba_store
1685         }
1686       when nil
1687         item.instance_variable_set('@feedbacks', [])
1688         item.odba_store
1689       end
1690     end
1691     def utf8ify(object, opts={})
1692       from = 'ISO-8859-1'
1693       to = 'UTF-8//TRANSLIT//IGNORE'
1694       if opts[:reverse]
1695         from, to = to, from
1696       end
1697       iconv = ::Iconv.new to, from
1698       _migrate_to_utf8([object], {}, iconv)
1699     end
1700     def migrate_to_utf8
1701       iconv = ::Iconv.new 'UTF-8//TRANSLIT//IGNORE', 'ISO-8859-1'
1702       ODBA.cache.retire_age = 5
1703       ODBA.cache.cleaner_step = 100000
1704       system = @system.odba_instance
1705       table = { system.odba_id => true, :serialized => {} }
1706       table.store :finalizer, proc { |object_id|
1707         table[:serialized].delete object_id }
1708       queue = [ system ]
1709       last_size = 0
1710       system.instance_variable_set '@config', nil
1711       while !queue.empty?
1712         if (queue.size - last_size).abs >= 10000
1713           puts last_size = queue.size
1714         end
1715         _migrate_to_utf8 queue, table, iconv, :all => true
1716       end
1717     end
1718     def _migrate_to_utf8 queue, table, iconv, opts={}
1719       obj = queue.shift
1720       if obj.is_a?(Numeric)
1721         begin
1722           obj = ODBA.cache.fetch obj
1723         rescue ODBA::OdbaError
1724           return
1725         end
1726       else
1727         obj = obj.odba_instance
1728       end
1729       _migrate_obj_to_utf8 obj, queue, table, iconv, opts
1730       obj.odba_store unless obj.odba_unsaved?
1731     end
1732     def _migrate_obj_to_utf8 obj, queue, table, iconv, opts={}
1733       obj.instance_variables.each do |name|
1734         child = obj.instance_variable_get name
1735         if child.respond_to?(:odba_unsaved?) && !child.odba_unsaved? \
1736           && obj.respond_to?(:odba_serializables) \
1737           && obj.odba_serializables.include?(name)
1738           child.instance_variable_set '@odba_persistent', nil
1739         end
1740         child = _migrate_child_to_utf8 child, queue, table, iconv, opts
1741         obj.instance_variable_set name, child
1742       end
1743       if obj.is_a?(Array)
1744         obj.collect! do |child|
1745           _migrate_child_to_utf8 child, queue, table, iconv, opts
1746         end
1747       end
1748       if obj.is_a?(Hash)
1749         obj.dup.each do |key, child|
1750           obj.store key, _migrate_child_to_utf8(child, queue, table, iconv, opts)
1751         end
1752         if obj.is_a?(ODDB::SimpleLanguage::Descriptions)
1753           obj.default = _migrate_child_to_utf8 obj.default, queue, table, iconv, opts
1754         end
1755       end
1756       obj
1757     end
1758     def _migrate_child_to_utf8 child, queue, table, iconv, opts={}
1759       @serialized ||= {}
1760       case child
1761       when ODBA::Persistable, ODBA::Stub
1762         if child = child.odba_instance
1763           if child.odba_unsaved?
1764             _migrate_to_utf8 [child], table, iconv, opts
1765           elsif opts[:all]
1766             odba_id = child.odba_id
1767             unless table[odba_id]
1768               table.store odba_id, true
1769               queue.push odba_id
1770             end
1771           end
1772         end
1773       when String
1774         child = iconv.iconv(child)
1775       when ODDB::Text::Section, ODDB::Text::Paragraph, ODDB::PatinfoDocument,
1776            ODDB::PatinfoDocument2001, ODDB::Text::Table, ODDB::Text::Cell,
1777            ODDB::Analysis::Permission, ODDB::Interaction::AbstractLink,
1778            ODDB::Dose
1779         child = _migrate_obj_to_utf8 child, queue, table, iconv, opts
1780       when ODDB::Address2
1781         ## Address2 may cause StackOverflow if not controlled
1782         unless table[:serialized][child.object_id]
1783           table[:serialized].store child.object_id, true
1784           ObjectSpace.define_finalizer child, table[:finalizer]
1785           child = _migrate_obj_to_utf8 child, queue, table, iconv, opts
1786         end
1787       when Float, Fixnum, TrueClass, FalseClass, NilClass,
1788         ODDB::Persistence::Pointer, Symbol, Time, Date, ODDB::Dose, Quanty,
1789         ODDB::Util::Money, ODDB::Fachinfo::ChangeLogItem, ODDB::AtcNode,
1790         DateTime, ODDB::NotificationLogger::LogEntry, ODDB::Text::Format,
1791         ODDB::YusStub, ODDB::Text::ImageLink
1792         # do nothing
1793       else
1794         @ignored ||= {}
1795         unless @ignored[child.class]
1796           @ignored.store child.class, true
1797           warn "ignoring #{child.class}"
1798         end
1799       end
1800       child
1801     rescue SystemStackError
1802       puts child.class
1803       raise
1804     end
1805 
1806     def log_size
1807       @size_logger = Thread.new {
1808         time = Time.now
1809         bytes = 0
1810         threads = 0
1811         sessions = 0
1812         format = "%s %s: sessions: %4i - threads: %4i - memory: %4iMB %s"
1813         loop {
1814           begin
1815             lasttime = time
1816             time = Time.now
1817             alarm = time - lasttime > 60 ? '*' : ' '
1818             lastthreads = threads
1819             threads = Thread.list.size
1820             lastbytes = bytes
1821             bytes = File.read("/proc/#{$$}/stat").split(' ').at(22).to_i
1822             mbytes = bytes / (2**20)
1823             if mbytes > MEMORY_LIMIT
1824               puts "Footprint exceeds #{MEMORY_LIMIT}MB. Exiting."
1825               Thread.main.raise SystemExit
1826             end
1827             lastsessions = sessions
1828             sessions = @sessions.size
1829             gc = ''
1830             gc << 'S' if sessions < lastsessions
1831             gc << 'T' if threads < lastthreads
1832             gc << 'M' if bytes < lastbytes
1833             path = File.expand_path('../../doc/resources/downloads/status',
1834                                     File.dirname(__FILE__))
1835             lines = File.readlines(path)[0,100] rescue []
1836             lines.unshift sprintf(format, alarm, 
1837                                   time.strftime('%Y-%m-%d %H:%M:%S'),
1838                                   sessions, threads, mbytes, gc)
1839             File.open(path, 'w') { |fh|
1840               fh.puts lines
1841             }
1842           rescue StandardError => e
1843             puts e.class
1844             puts e.message
1845             $stdout.flush
1846           end
1847         sleep 5
1848         }
1849       }
1850     end
1851 	end
1852 end
1853 
1854 begin 
1855 	require 'testenvironment'
1856 rescue LoadError
1857 end

Generated on Fri Feb 18 16:44:24 +0100 2011 with rcov 0.9.8