Skip to content
Snippets Groups Projects
Commit 4ec988f3 authored by Frank Winklmeier's avatar Frank Winklmeier
Browse files

Merge branch 'sweep_24.0_main_2024-03-21' into 'main'

2024-03-21: merge of 24.0 into main

See merge request atlas/athena!70026
parents b3d8be6b 70710ec3
No related branches found
No related tags found
No related merge requests found
Showing
with 154 additions and 210 deletions
......@@ -172,11 +172,14 @@ class FlagAddress(object):
def __iter__(self):
self._flags.loadAllDynamicFlags()
rmap = self._flags._renamed_map()
used = set()
for flag in self._flags._flagdict.keys():
if flag.startswith(self._name.rstrip('.') + '.'):
newflag = rmap[flag]
ntrim = len(self._name) + 1
remaining = flag[ntrim:].split('.',1)[0]
n_dots_in = flag[:ntrim].count('.')
remaining = newflag.split('.')[n_dots_in]
if remaining not in used:
yield remaining
used.add(remaining)
......@@ -290,9 +293,10 @@ class AthConfigFlags(object):
def __iter__(self):
self.loadAllDynamicFlags()
rmap = self._renamed_map()
used = set()
for flag in self._flagdict:
first = flag.split('.',1)[0]
first = rmap[flag].split('.',1)[0]
if first not in used:
yield first
used.add(first)
......@@ -308,6 +312,11 @@ class AthConfigFlags(object):
def _renamed_map(self):
"""mapping from the old names to the new names
This is the inverse of _renamed, which maps new names to old
names
"""
def rename(key):
for new, old in self._renames.items():
if key.startswith(old + '.'):
......@@ -420,7 +429,7 @@ class AthConfigFlags(object):
return False
def hasFlag(self, name):
return name in self._flagdict
return name in self._renamed_map().values()
def _set(self,name,value):
self._tryModify()
......@@ -465,6 +474,7 @@ class AthConfigFlags(object):
cln = AthConfigFlags()
cln._flagdict = deepcopy(self._flagdict)
cln._dynaflags = copy(self._dynaflags)
cln._renames = deepcopy(self._renames)
return cln
......
......@@ -324,6 +324,30 @@ class TestFlagsSetupDynamic(FlagsSetup):
self.assertTrue( self.flags.hasFlag("Z.A") )
self.assertTrue( self.flags.hasCategory("Z.C") )
def test_cloneExists(self):
"""test if flags can be found after cloning"""
clonef = self.flags.cloneAndReplace('W', 'Z')
clonef.loadAllDynamicFlags()
self.assertTrue(clonef.hasFlag('W.A'))
self.assertFalse(clonef.hasFlag('Z.A'))
def test_cloneIter(self):
# top level check
self.assertTrue('Z' in self.flags)
self.assertTrue('A' in self.flags.Z)
clonez2w = self.flags.cloneAndReplace('W', 'Z')
self.assertFalse('Z' in clonez2w)
self.assertTrue('W' in clonez2w)
self.assertFalse('Z' in clonez2w.W)
self.assertTrue('A' in clonez2w.W)
# check one level down
self.assertTrue('C' in self.flags.Z)
clonec2x = self.flags.cloneAndReplace('Z.X', 'Z.C')
self.assertTrue('X' in clonec2x.Z)
self.assertFalse('C' in clonec2x.Z)
def test_cloneHash(self):
# compare copy hash to clone hash, should be equal
copyflags = copy.deepcopy(self.flags)
......
## @file: AthenaPython/copy_file.py
## @purpose: simple jobo to copy any file, leveraging the auto-config fwk
## @date January 2010
## @author Sebastien Binet <binet@cern.ch>
__version__ = "$Revision: 293864 $"
__author__ = "Sebastien Binet <binet@cern.ch>"
__doc__ = "simple jobo to copy any file, leveraging the auto-config fwk"
## percolate through the auto-configuration
## input files configuration
from AthenaCommon.AthenaCommonFlags import jobproperties as jp
acf = jp.AthenaCommonFlags
assert len(acf.FilesInput()) != 0, \
"this jobo fragment needs the autoconfig-fwk." \
"FilesInput needs to be filled"
import AthenaPython.ConfigLib as apcl
cfg = apcl.copy_file(src=acf.FilesInput(),
dst="copy_file.pool")
if cfg.is_rdo() or cfg.is_esd() or cfg.is_aod():
# main jobos
include ('RecExCond/RecExCommon_flags.py')
include ('RecExCommon/RecExCommon_topOptions.py')
## @file: AthenaPython/icopy_file.py
## @purpose: simple jobo to copy any file, leveraging the auto-config fwk
## @date January 2010
## @author Sebastien Binet <binet@cern.ch>
__version__ = "$Revision: 293864 $"
__author__ = "Sebastien Binet <binet@cern.ch>"
__doc__ = "simple jobo to copy any file, leveraging the auto-config fwk"
## percolate through the auto-configuration
## input files configuration
from AthenaCommon.AthenaCommonFlags import jobproperties as jp
acf = jp.AthenaCommonFlags
_input_files = globals()['FNAME']
if isinstance(_input_files, str):
_input_files = [_input_files]
acf.FilesInput = _input_files
del _input_files
# events to process
acf.EvtMax = EvtMax = theApp.EvtMax = globals().get('EVTMAX', -1)
import AthenaPython.ConfigLib as apcl
cfg = apcl.AutoCfg(name='copy-file',
input_files=acf.FilesInput(),
output_file=globals().get('OFNAME', 'copy_file.pool'))
cfg.configure_job()
if cfg.is_rdo() or cfg.is_esd() or cfg.is_aod():
# main jobos
include ('RecExCond/RecExCommon_flags.py')
include ('RecExCommon/RecExCommon_topOptions.py')
## @file: AthenaPython/iread_file.py
## @purpose: simple jobo to read any file, leveraging the auto-config fwk
## @date January 2010
## @author Sebastien Binet <binet@cern.ch>
__version__ = "$Revision: 273745 $"
__author__ = "Sebastien Binet <binet@cern.ch>"
__doc__ = "simple jobo to read any file, leveraging the auto-config fwk"
## percolate through the auto-configuration
## input files configuration
from AthenaCommon.AthenaCommonFlags import jobproperties as jp
acf = jp.AthenaCommonFlags
_input_files = globals().get('FNAME', [])
if isinstance(_input_files, str):
_input_files = [_input_files]
acf.FilesInput = _input_files
del _input_files
# events to process
acf.EvtMax = EvtMax = globals().get('EVTMAX', -1)
# main jobos
include('AthenaPython/read_file.py')
## @file: AthenaPython/read_file.py
## @purpose: simple jobo to read any file, leveraging the auto-config fwk
## @date November 2009
## @author Sebastien Binet <binet@cern.ch>
__version__ = "$Revision: 279865 $"
__author__ = "Sebastien Binet <binet@cern.ch>"
__doc__ = "simple jobo to read any file, leveraging the auto-config fwk"
## percolate through the auto-configuration
## input files configuration
from AthenaCommon.AthenaCommonFlags import jobproperties as jp
acf = jp.AthenaCommonFlags
assert len(acf.FilesInput()) != 0, \
"this jobo fragment needs the autoconfig-fwk." \
"FilesInput needs to be filled"
from AthenaConfiguration.AllConfigFlags import ConfigFlags
ConfigFlags.Input.Files = acf.FilesInput()
import AthenaPython.ConfigLib as apcl
cfg = apcl.AutoCfg(name='read-file',
input_files=acf.FilesInput())
cfg.configure_job()
if not cfg.is_evgen():
# main jobos
include ('RecExCond/RecExCommon_flags.py')
include ('RecExCommon/RecExCommon_topOptions.py')
/*
Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
*/
#ifndef STOREGATE_HIVEMGRSVC_H
......@@ -19,17 +19,19 @@
class StoreGateSvc;
class ISvcLocator;
class HltEventLoopMgr; // friend
class HltAsyncEventLoopMgr; // friend
/** @class HiveMgrSvc
* @brief A service that manages a multi-event collection of StoreGateSvc
* It implements the IHiveWhiteBoard interface
*
* $Id: SGHiveMgrSvc.h 794852 2017-01-31 23:24:04Z leggett $
**/
namespace SG {
class HiveMgrSvc : public extends<Service, IHiveWhiteBoard> {
friend class TestSGHiveMgrSvc;
friend class ::HltEventLoopMgr;
friend class ::HltAsyncEventLoopMgr;
public:
//@{ @name IHiveWhiteBoard implementation
/** Activate an given 'slot' for all subsequent calls within the
......@@ -106,12 +108,21 @@ public:
virtual ~HiveMgrSvc() {}
private:
/** Set number of concurrent processes
*
* This can only be called by "friends" of this class. Its sole purpose
* is to have a common entry point within ATLAS to call the private
* methods of Gaudi::ConcurrencyFlags.
*
* @param numProcs [IN] Number of concurrent processes
*/
static void setNumProcs(size_t numProcs);
ServiceHandle<StoreGateSvc> m_hiveStore;
size_t m_nSlots; //property settable also by setNumberOfStores
std::vector<SG::HiveEventSlot> m_slots;
std::mutex m_mutex; //< protects m_slots access
std::atomic<size_t> m_freeSlots {0};
//maybe ServiceHandle<ActiveStoreSvc> m_active;
};
} //namespace SG
......
/*
Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
*/
#include "GaudiKernel/ConcurrencyFlags.h"
......@@ -8,7 +8,7 @@
#include "AthenaKernel/StoreID.h"
#include "StoreGate/StoreGateSvc.h"
#include "StoreGate/tools/SGImplSvc.h"
#include "SGHiveMgrSvc.h"
#include "StoreGate/SGHiveMgrSvc.h"
using namespace SG;
......@@ -24,6 +24,18 @@ HiveMgrSvc::HiveMgrSvc(const std::string& name,
}
/** Set number of concurrent processes
*
* This can only be called by "friends" of this class. Its sole purpose
* is to have a common entry point within ATLAS to call the private
* methods of Gaudi::ConcurrencyFlags.
*
* @param numProcs [IN] Number of concurrent processes
*/
void HiveMgrSvc::setNumProcs(size_t numProcs)
{
Gaudi::Concurrency::ConcurrencyFlags::setNumProcs(numProcs);
}
/** Activate an given 'slot' for all subsequent calls within the
* same thread id.
......
......@@ -2,7 +2,7 @@
#include "StoreGate/ActiveStoreSvc.h"
#include "StoreGate/tools/SGImplSvc.h"
#include "StoreGate/SegMemSvc.h"
#include "../SGHiveMgrSvc.h"
#include "StoreGate/SGHiveMgrSvc.h"
DECLARE_COMPONENT( ActiveStoreSvc )
DECLARE_COMPONENT( StoreGateSvc )
......
/*
Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
*/
/***************************************************************************
......@@ -14,7 +14,7 @@
#include "TestTools/initGaudi.h"
#include "TestTools/SGassert.h"
#include "GaudiKernel/IHiveWhiteBoard.h"
#include "../src/SGHiveMgrSvc.h"
#include "StoreGate/SGHiveMgrSvc.h"
#include "StoreGate/StoreGateSvc.h"
#include "StoreGate/SGtests.h"
......
/*
Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
*/
#undef NDEBUG
......@@ -10,7 +10,7 @@
#include "TestTools/initGaudi.h"
#include "TestTools/SGassert.h"
#include "GaudiKernel/IHiveWhiteBoard.h"
#include "../src/SGHiveMgrSvc.h"
#include "StoreGate/SGHiveMgrSvc.h"
#include "StoreGate/StoreGateSvc.h"
#include "StoreGate/SGtests.h"
......
......@@ -107,6 +107,10 @@ output top_level {
}
}
}
output CrossBar {
output side${side} {
}
}
}
output ToFSiTCorr {
}
......@@ -538,6 +542,14 @@ dir AFP {
}
}
}
dir CrossBar {
dir side(?P<side>A|C) {
regex = 1
output = AFP/ToF/CrossBar/side${side}
hist crossBarDeltaT_(?P=side)_[0123](AB|AC|AD|BC|BD|CD) {
}
}
}
}
dir ToFSiTCorr {
output = AFP/ToFSiTCorr
......
......@@ -32,6 +32,7 @@
#include <TStyle.h>
#include <TText.h>
#include <TImageDump.h>
#include <TFrame.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/lexical_cast.hpp>
......@@ -633,7 +634,7 @@ namespace dqutils
return "Undefined";
}
// Extract JSON object
TObjString* JSON_obj = dynamic_cast<TObjString*>(gDirectory->GetKey(JSON_name.c_str())->ReadObj());
std::unique_ptr<TObjString> JSON_obj(dynamic_cast<TObjString*>(gDirectory->GetKey(JSON_name.c_str())->ReadObj()));
if (not JSON_obj)
{
std::cerr << "HanOutputFile::getInfo : dynamic cast failed\n";
......@@ -1052,6 +1053,7 @@ namespace dqutils
{
o << " title " << hisTitle << "\n";
}
delete h;
}
else
{
......@@ -1892,7 +1894,7 @@ namespace dqutils
if (colln)
{
WasCollectionReference = true;
TIterator* icolln = colln->MakeIterator();
std::unique_ptr<TIterator> icolln(colln->MakeIterator());
TObject* ref2;
while ((ref2 = icolln->Next()))
{
......@@ -2300,7 +2302,7 @@ namespace dqutils
//deallocate image buffer with free(x), see https://root.cern.ch/doc/master/classTASImage.html
free(x);
delete hobj;
delete hRef;
delete ref;
delete legend;
return rvPair;
}
......@@ -3188,36 +3190,41 @@ namespace dqutils
//////
TProfile* phRef = dynamic_cast<TProfile*>(hRef);
TProfile* ph = dynamic_cast<TProfile*>(h);
TH1F* clonehist;
TH1F* clonehistref;
std::unique_ptr<TH1F> clonehist; // we will release this later, but use a unique_ptr in case we return early
std::unique_ptr<TH1F> clonehistref;
// transform if profiles
if (ph != 0)
{
clonehist = (TH1F*)ph->ProjectionX();
clonehist.reset((TH1F*)ph->ProjectionX());
}
else
{
clonehist = (TH1F*)h->Clone();
clonehist->Sumw2();
clonehist.reset((TH1F*)h->Clone());
if (!clonehist->GetSumw2()) {
clonehist->Sumw2();
}
}
if (phRef != 0)
{
clonehistref = (TH1F*)phRef->ProjectionX();
clonehistref.reset((TH1F*)phRef->ProjectionX());
}
else
{
clonehistref = (TH1F*)hRef->Clone();
clonehistref->Sumw2();
clonehistref.reset((TH1F*)hRef->Clone());
if (!clonehist->GetSumw2()) {
clonehistref->Sumw2();
}
}
if (!clonehist or !clonehistref)
{
return;
}
clonehist->Divide(clonehistref);
clonehist->SetBit(kCanDelete);
clonehist->Divide(clonehistref.get());
/// Error Bars fixed
//////
formatTH1(myC_ratiopad.get(), clonehist);
formatTH1(myC_ratiopad.get(), clonehist.get());
clonehist->SetTitle("");
// extract delta value from string that holds the draw options
......@@ -3230,42 +3237,69 @@ namespace dqutils
clonehist->GetYaxis()->SetNdivisions(3, true);
clonehist->SetMarkerStyle(1);
clonehist->Draw("E");
clonehist->Draw("E"); // plots into myC_ratiopad
clonehist->GetXaxis()->SetTitleSize(0.11);
clonehist->GetXaxis()->SetLabelSize(0.11);
clonehist->GetYaxis()->SetTitleSize(0.11);
clonehist->GetYaxis()->SetLabelSize(0.11);
myC_main->cd();
TPad* lowerPad = new TPad("lowerPad", "lowerPad", .005, .060, .995, .250);
myC_main->cd(); // lowerPad and upperPad plotted into myC_main
TPad* lowerPad = new TPad("lowerPad", "lowerPad", .005, .060, .995, .250); // deleted by myC_main
lowerPad->SetTopMargin(0);
lowerPad->SetFillStyle(0);
lowerPad->Draw();
TPad* upperPad = new TPad("upperPad", "upperPad", .005, .250, .995, .995);
TPad* upperPad = new TPad("upperPad", "upperPad", .005, .250, .995, .995); // deleted by myC_main
upperPad->SetBottomMargin(0);
upperPad->SetFillStyle(0);
upperPad->Draw();
lowerPad->cd();
myC_ratiopad->DrawClonePad();
myC_ratiopad->DrawClonePad(); // clone contents of myC_ratiopad to lowerPad (will fix ownership later)
// Draw y=1 lineon ratio plot
TLine* line = new TLine;
line->SetLineColor(kRed);
line->SetLineWidth(1);
TLine line;
line.SetLineColor(kRed);
line.SetLineWidth(1);
// method belove might be a problem when axis range changed
double xmin = clonehist->GetXaxis()->GetXmin();
double xmax = clonehist->GetXaxis()->GetXmax();
// double xmin = BINLOEDGE(clonehist, 1)-BINWIDTH(clonehist, 1);
// double xmax = BINLOEDGE(clonehist, clonehist->GetNbinsX() ) + 2.0*BINWIDTH(clonehist, clonehist->GetNbinsX() ) ;
line->DrawLine(xmin, 1, xmax, 1);
line.DrawLine(xmin, 1, xmax, 1);
upperPad->cd();
myC_upperpad->SetBottomMargin(0);
myC_upperpad->SetFillStyle(0);
h->GetXaxis()->SetLabelSize(0.);
h->GetXaxis()->SetTitleSize(0.);
myC_upperpad->DrawClonePad();
myC_upperpad->DrawClonePad(); // clone original canvas (i.e. main plot) into upperPad (will fix ownership later)
myC_upperpad->cd();
myC_upperpad->Clear();
myC_main->DrawClonePad();
myC_upperpad->Clear(); // reset original canvas
myC_main->DrawClonePad(); // clone contents of myC_main back into original canvas (will fix ownership shortly)
clonehist.release(); // this will be deleted by lowerpad cleanup
// At this point myC_main contains the original lowerPad and upperPad, which contain clones of the original canvas
// and ownership of clonehist. Iterate one level down and mark contained objects as deleteable. This will delete
// the pads, as well as clonehist.
for (TObject* o : *(myC_main->GetListOfPrimitives())) {
o->SetBit(kCanDelete);
if (auto* o2 = dynamic_cast<TPad*>(o)) {
for (auto* o3: *(o2->GetListOfPrimitives())) {
if (!dynamic_cast<TFrame*>(o3)) {
o3->SetBit(kCanDelete);
}
}
}
}
// At this point myC_upperpad contains clones of all its objects, including the pads. None of them have pointers
// outside of myC_upperpad and its contained cloned pads. Mark them all deleteable. The original plot will be deleted
// in the calling code.
for (TObject* o : *(myC_upperpad->GetListOfPrimitives())) {
o->SetBit(kCanDelete);
if (auto* o2 = dynamic_cast<TPad*>(o)) {
for (auto* o3: *(o2->GetListOfPrimitives())) {
if (!dynamic_cast<TFrame*>(o3)) {
o3->SetBit(kCanDelete);
}
}
}
}
}
void HanOutputFile::ratioplot2D(TCanvas* canvas_top, TH2* h2, TH2* h2Ref, std::string display)
......
......@@ -138,14 +138,6 @@ namespace xAOD {
int16_t gFexJetRoI_v1::unpackEt() const {
// Data content = TOB
int16_t energy = (word() >> s_etBit) & s_etMask;
int SIGNMASK = 0x0800;
int EXTENDS = 0xF000;
if (gFexType() == gRho){
if( (SIGNMASK & energy ) ) {
energy = ( EXTENDS | energy);
}
}
return energy;
}
......
# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
# Declare the package name:
atlas_subdir( AFP_Digitization )
......@@ -16,4 +16,3 @@ atlas_add_component( AFP_Digitization
# Install files from the package:
atlas_install_python_modules( python/*.py POST_BUILD_CMD ${ATLAS_FLAKE8} )
atlas_install_joboptions( share/*.py )
from AthenaCommon.AlgSequence import AlgSequence
job = AlgSequence()
from AthenaCommon import CfgGetter
job += CfgGetter.getAlgorithm("AFP_DigiTop/AFP_DigiTop", tryDefaultConfigurable=True)
afp = job.AFP_DigiTop.DigitizationTool
from AthenaCommon.AlgSequence import AlgSequence
job = AlgSequence()
from AthenaCommon import CfgGetter
job.PileUpToolsAlg.PileUpTools += [ CfgGetter.getPrivateTool("AFP_PileUpTool", checkType=True) ]
afpPileUpTool = job.PileUpToolsAlg.PileUpTools[ "AFP_PileUpTool" ]
from AthenaCommon.AthenaCommonFlags import jobproperties
jobproperties.AthenaCommonFlags.PoolHitsInput = ["atlasG4.hits.pool.root"]
jobproperties.AthenaCommonFlags.PoolRDOOutput = "digi.pool.root"
jobproperties.AthenaCommonFlags.EvtMax = -1
from AthenaCommon.GlobalFlags import jobproperties
jobproperties.Global.DetDescrVersion = 'ATLAS-GEO-20-00-01'
from AthenaCommon.DetFlags import DetFlags
DetFlags.AFP_setOn()
DetFlags.Truth_setOn()
include("Digitization/Digitization.py")
Service("StoreGateSvc").ActivateHistory = False
Service("GeoModelSvc").IgnoreTagDifference = True
# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
from IOVDbSvc.CondDB import conddb
from AFP_GeoModel.AFP_GeoModelConf import AFP_DetectorTool
def getAFP_DetectorTool(name="AFP_DetectorTool"):
#instatiate the tool
theAFP_DetectorTool=AFP_DetectorTool(name)
return theAFP_DetectorTool
......@@ -10,6 +10,8 @@
#include "StoreGate/ReadHandleKey.h"
#include "xAODForward/AFPToFHitContainer.h"
#include "xAODForward/AFPToFHit.h"
#include "xAODForward/AFPTrackContainer.h"
#include "xAODForward/AFPTrack.h"
#include "LumiBlockData/BunchCrossingCondData.h"
#include "TRandom3.h"
......@@ -20,13 +22,16 @@ public:
virtual ~AFPToFAlgorithm();
virtual StatusCode initialize() override;
virtual StatusCode fillHistograms( const EventContext& ctx ) const override;
virtual StatusCode fillHistograms_crossBarDeltaT( const xAOD::AFPTrackContainer&, const xAOD::AFPToFHitContainer& ) const;
private:
std::map<std::string,int> m_StationNamesGroup;
std::map<std::string,int> m_TrainsToFGroup;
std::map<std::string,std::map<std::string,int>> m_BarsInTrainsA;
std::map<std::string,std::map<std::string,int>> m_BarsInTrainsC;
std::map<std::string,int> m_GroupChanCombDeltaT;
SG::ReadHandleKey<xAOD::AFPToFHitContainer> m_afpToFHitContainerKey;
SG::ReadHandleKey<xAOD::AFPTrackContainer> m_afpTrackContainerKey;
SG::ReadCondHandleKey<BunchCrossingCondData> m_bunchCrossingKeyToF{this, "BunchCrossingKey", "BunchCrossingData", "Key BunchCrossing CDO" };
protected:
......@@ -37,7 +42,12 @@ protected:
std::vector<std::string> m_trainsToFA = { "T0", "T1" , "T2" , "T3" };
std::vector<std::string> m_trainsToFC = { "T0", "T1" , "T2" , "T3" };
std::vector<std::string> m_barsToF = { "A", "B" , "C" , "D" };
std::vector<std::string> m_chanComb = {
"0AB", "0AC", "0AD", "0BC", "0BD", "0CD",
"1AB", "1AC", "1AD", "1BC", "1BD", "1CD",
"2AB", "2AC", "2AD", "2BC", "2BD", "2CD",
"3AB", "3AC", "3AD", "3BC", "3BD", "3CD"};
};
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment