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

Generated on Thu Mar 03 09:26:31 +0100 2011 with rcov 0.9.8